# HG changeset patch
# User RĂ©mi Cardona %s %s an error occurred while interpreting this "
- "rql directive: ObjectNotFound(u'toto',)Welcome to the "%s" community
' % entity.printable_value('name'))
if entity.display_cw_logo():
- self.w(u'')
+ self.w(u'')
if entity.description:
self.w(u'
héhéhé
') @@ -717,7 +721,7 @@ e = self.vreg['etypes'].etype_class('FakeFile')(req) e.cw_attr_cache['description'] = 'du html' e.cw_attr_cache['description_format'] = 'text/html' - e.cw_attr_cache['data'] = Binary('some data') + e.cw_attr_cache['data'] = Binary(b'some data') e.cw_attr_cache['data_name'] = 'an html file' e.cw_attr_cache['data_format'] = 'text/html' e.cw_attr_cache['data_encoding'] = 'ascii' @@ -769,11 +773,11 @@ # ambiguity test person2 = req.create_entity('Personne', prenom=u'remi', nom=u'doe') person.cw_clear_all_caches() - self.assertEqual(person.rest_path(), unicode(person.eid)) - self.assertEqual(person2.rest_path(), unicode(person2.eid)) + self.assertEqual(person.rest_path(), text_type(person.eid)) + self.assertEqual(person2.rest_path(), text_type(person2.eid)) # unique attr with None value (nom in this case) friend = req.create_entity('Ami', prenom=u'bob') - self.assertEqual(friend.rest_path(), unicode(friend.eid)) + self.assertEqual(friend.rest_path(), text_type(friend.eid)) # 'ref' below is created without the unique but not required # attribute, make sur that the unique _and_ required 'ean' is used # as the rest attribute @@ -853,4 +857,3 @@ if __name__ == '__main__': from logilab.common.testlib import unittest_main unittest_main() - diff -r 3a98422df969 -r 7ceb0971c694 test/unittest_mail.py --- a/test/unittest_mail.py Tue Dec 15 19:05:41 2015 +0100 +++ b/test/unittest_mail.py Wed Dec 16 11:23:48 2015 +0100 @@ -149,4 +149,3 @@ if __name__ == '__main__': unittest_main() - diff -r 3a98422df969 -r 7ceb0971c694 test/unittest_migration.py --- a/test/unittest_migration.py Tue Dec 15 19:05:41 2015 +0100 +++ b/test/unittest_migration.py Wed Dec 16 11:23:48 2015 +0100 @@ -1,4 +1,4 @@ -# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -22,7 +22,7 @@ from cubicweb.devtools import TestServerConfiguration from cubicweb.cwconfig import CubicWebConfiguration -from cubicweb.migration import MigrationHelper, filter_scripts +from cubicweb.migration import MigrationHelper, filter_scripts, version_strictly_lower from cubicweb.server.migractions import ServerMigrationHelper @@ -76,8 +76,6 @@ def test_filter_scripts_for_mode(self): config = CubicWebConfiguration('data') config.verbosity = 0 - self.assertNotIsInstance(config.migration_handler(), ServerMigrationHelper) - self.assertIsInstance(config.migration_handler(), MigrationHelper) config = self.config config.__class__.name = 'repository' self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)), @@ -91,6 +89,10 @@ ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py')]) config.__class__.name = 'repository' + def test_version_strictly_lower(self): + self.assertTrue(version_strictly_lower(None, '1.0.0')) + self.assertFalse(version_strictly_lower('1.0.0', None)) + from cubicweb.devtools import ApptestConfiguration, get_test_db_handler diff -r 3a98422df969 -r 7ceb0971c694 test/unittest_predicates.py --- a/test/unittest_predicates.py Tue Dec 15 19:05:41 2015 +0100 +++ b/test/unittest_predicates.py Wed Dec 16 11:23:48 2015 +0100 @@ -20,14 +20,17 @@ from operator import eq, lt, le, gt from contextlib import contextmanager +from six.moves import range + from logilab.common.testlib import TestCase, unittest_main from logilab.common.decorators import clear_cache from cubicweb import Binary from cubicweb.devtools.testlib import CubicWebTC from cubicweb.predicates import (is_instance, adaptable, match_kwargs, match_user_groups, - multi_lines_rset, score_entity, is_in_state, - rql_condition, relation_possible, match_form_params) + multi_lines_rset, score_entity, is_in_state, + rql_condition, relation_possible, match_form_params, + paginated_rset) from cubicweb.selectors import on_transition # XXX on_transition is deprecated from cubicweb.view import EntityAdapter from cubicweb.web import action @@ -37,7 +40,7 @@ class ImplementsTC(CubicWebTC): def test_etype_priority(self): with self.admin_access.web_request() as req: - f = req.create_entity('FakeFile', data_name=u'hop.txt', data=Binary('hop'), + f = req.create_entity('FakeFile', data_name=u'hop.txt', data=Binary(b'hop'), data_format=u'text/plain') rset = f.as_rset() anyscore = is_instance('Any')(f.__class__, req, rset=rset) @@ -488,6 +491,34 @@ "match_form_params() positional arguments must be strings") +class PaginatedTC(CubicWebTC): + """tests for paginated_rset predicate""" + + def setup_database(self): + with self.admin_access.repo_cnx() as cnx: + for i in range(30): + cnx.create_entity('CWGroup', name=u"group%d" % i) + cnx.commit() + + def test_paginated_rset(self): + default_nb_pages = 1 + web_request = self.admin_access.web_request + with web_request() as req: + rset = req.execute('Any G WHERE G is CWGroup') + self.assertEqual(len(rset), 34) + with web_request(vid='list', page_size='10') as req: + self.assertEqual(paginated_rset()(None, req, rset), default_nb_pages) + with web_request(vid='list', page_size='20') as req: + self.assertEqual(paginated_rset()(None, req, rset), default_nb_pages) + with web_request(vid='list', page_size='50') as req: + self.assertEqual(paginated_rset()(None, req, rset), 0) + with web_request(vid='list', page_size='10/') as req: + self.assertEqual(paginated_rset()(None, req, rset), 0) + with web_request(vid='list', page_size='.1') as req: + self.assertEqual(paginated_rset()(None, req, rset), 0) + with web_request(vid='list', page_size='not_an_int') as req: + self.assertEqual(paginated_rset()(None, req, rset), 0) + + if __name__ == '__main__': unittest_main() - diff -r 3a98422df969 -r 7ceb0971c694 test/unittest_rqlrewrite.py --- a/test/unittest_rqlrewrite.py Tue Dec 15 19:05:41 2015 +0100 +++ b/test/unittest_rqlrewrite.py Wed Dec 16 11:23:48 2015 +0100 @@ -16,6 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see')
- jscall = unicode(js.ajaxBoxShowSelector(
+ jscall = text_type(js.ajaxBoxShowSelector(
self.__regid__, entity.eid, self.fname_vocabulary,
self.fname_validate, self.added_msg and _(self.added_msg),
_(stdmsgs.BUTTON_OK[0]), _(stdmsgs.BUTTON_CANCEL[0]),
@@ -677,6 +682,7 @@
# old contextual components, deprecated ########################################
+@add_metaclass(class_deprecated)
class EntityVComponent(Component):
"""abstract base class for additinal components displayed in content
headers and footer according to:
@@ -687,7 +693,6 @@
it should be configured using .accepts, .etype, .rtype, .target and
.context class attributes
"""
- __metaclass__ = class_deprecated
__deprecation_warning__ = '[3.10] *VComponent classes are deprecated, use *CtxComponent instead (%(cls)s)'
__registry__ = 'ctxcomponents'
diff -r 3a98422df969 -r 7ceb0971c694 web/controller.py
--- a/web/controller.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/controller.py Wed Dec 16 11:23:48 2015 +0100
@@ -19,6 +19,8 @@
__docformat__ = "restructuredtext en"
+from six import PY2
+
from logilab.mtconverter import xml_escape
from logilab.common.registry import yes
from logilab.common.deprecation import deprecated
@@ -87,7 +89,7 @@
rql = req.form.get('rql')
if rql:
req.ensure_ro_rql(rql)
- if not isinstance(rql, unicode):
+ if PY2 and not isinstance(rql, unicode):
rql = unicode(rql, req.encoding)
pp = req.vreg['components'].select_or_none('magicsearch', req)
if pp is not None:
@@ -132,8 +134,6 @@
newparams['_cwmsgid'] = self._cw.set_redirect_message(msg)
if '__action_apply' in self._cw.form:
self._return_to_edition_view(newparams)
- if '__action_cancel' in self._cw.form:
- self._return_to_lastpage(newparams)
else:
self._return_to_original_view(newparams)
@@ -155,7 +155,7 @@
and '_cwmsgid' in newparams):
# are we here on creation or modification?
if any(eid == self._edited_entity.eid
- for eid in self._cw.data.get('eidmap', {}).itervalues()):
+ for eid in self._cw.data.get('eidmap', {}).values()):
msg = self._cw._('click here to see created entity')
else:
msg = self._cw._('click here to see edited entity')
@@ -201,11 +201,9 @@
raise Redirect(self._cw.build_url(path, **newparams))
- def _return_to_lastpage(self, newparams):
- """cancel-button case: in this case we are always expecting to go back
- where we came from, and this is not easy. Currently we suppose that
- __redirectpath is specifying that place if found, else we look in the
- request breadcrumbs for the last visited page.
+ def _redirect(self, newparams):
+ """Raise a redirect. We use __redirectpath if it specified, else we
+ return to the home page.
"""
if '__redirectpath' in self._cw.form:
# if redirect path was explicitly specified in the form, use it
@@ -213,7 +211,7 @@
url = self._cw.build_url(path)
url = append_url_params(url, self._cw.form.get('__redirectparams'))
else:
- url = self._cw.last_visited_page()
+ url = self._cw.base_url()
# The newparams must update the params in all cases
url = self._cw.rebuild_url(url, **newparams)
raise Redirect(url)
@@ -221,4 +219,3 @@
from cubicweb import set_log_methods
set_log_methods(Controller, LOGGER)
-
diff -r 3a98422df969 -r 7ceb0971c694 web/cors.py
--- a/web/cors.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/cors.py Wed Dec 16 11:23:48 2015 +0100
@@ -14,7 +14,7 @@
"""
-import urlparse
+from six.moves.urllib.parse import urlsplit
from cubicweb.web import LOGGER
info = LOGGER.info
@@ -37,7 +37,7 @@
In case of non-compliance, no CORS-related header is set.
"""
- base_url = urlparse.urlsplit(req.base_url())
+ base_url = urlsplit(req.base_url())
expected_host = '://'.join((base_url.scheme, base_url.netloc))
if not req.get_header('Origin') or req.get_header('Origin') == expected_host:
# not a CORS request, nothing to do
@@ -50,7 +50,7 @@
process_preflight(req, config)
else: # Simple CORS or actual request
process_simple(req, config)
- except CORSFailed, exc:
+ except CORSFailed as exc:
info('Cross origin resource sharing failed: %s' % exc)
except CORSPreflight:
info('Cross origin resource sharing: valid Preflight request %s')
@@ -101,7 +101,7 @@
if '*' not in allowed_origins and origin not in allowed_origins:
raise CORSFailed('Origin is not allowed')
# bit of sanity check; see "6.3 Security"
- myhost = urlparse.urlsplit(req.base_url()).netloc
+ myhost = urlsplit(req.base_url()).netloc
host = req.get_header('Host')
if host != myhost:
info('cross origin resource sharing detected possible '
@@ -111,4 +111,3 @@
# include "Vary: Origin" header (see 6.4)
req.headers_out.addHeader('Vary', 'Origin')
return origin
-
diff -r 3a98422df969 -r 7ceb0971c694 web/data/cubicweb.edition.js
--- a/web/data/cubicweb.edition.js Tue Dec 15 19:05:41 2015 +0100
+++ b/web/data/cubicweb.edition.js Wed Dec 16 11:23:48 2015 +0100
@@ -537,6 +537,24 @@
}
/**
+ * Cancel the operations done on the given form.
+ *
+ */
+$(function () {
+ $(document).on('click', '.cwjs-edition-cancel', function (evt) {
+ var $mynode = $(evt.currentTarget),
+ $form = $mynode.closest('form'),
+ $error = $form.find(':input[name="__errorurl"]'),
+ errorurl = $error.attr('value'),
+ args = ajaxFuncArgs('cancel_edition', null, errorurl);
+ loadRemote(AJAX_BASE_URL, args, 'POST', true);
+ history.back();
+ return false;
+ });
+});
+
+
+/**
* .. function:: validateForm(formid, action, onsuccess, onfailure)
*
* called on traditionnal form submission : the idea is to try
diff -r 3a98422df969 -r 7ceb0971c694 web/facet.py
--- a/web/facet.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/facet.py Wed Dec 16 11:23:48 2015 +0100
@@ -50,13 +50,15 @@
"""
__docformat__ = "restructuredtext en"
-_ = unicode
+from cubicweb import _
from functools import reduce
from warnings import warn
from copy import deepcopy
from datetime import datetime, timedelta
+from six import text_type, string_types
+
from logilab.mtconverter import xml_escape
from logilab.common.graph import has_path
from logilab.common.decorators import cached, cachedproperty
@@ -80,7 +82,7 @@
ptypes = facet.cw_rset.column_types(0)
if len(ptypes) == 1:
return display_name(facet._cw, facet.rtype, form=facet.role,
- context=iter(ptypes).next())
+ context=next(iter(ptypes)))
return display_name(facet._cw, facet.rtype, form=facet.role)
def get_facet(req, facetid, select, filtered_variable):
@@ -133,7 +135,7 @@
or the first variable selected in column 0
"""
if mainvar is None:
- vref = select.selection[0].iget_nodes(nodes.VariableRef).next()
+ vref = next(select.selection[0].iget_nodes(nodes.VariableRef))
return vref.variable
return select.defined_vars[mainvar]
@@ -156,7 +158,7 @@
for term in select.selection[:]:
select.remove_selected(term)
# remove unbound variables which only have some type restriction
- for dvar in list(select.defined_vars.itervalues()):
+ for dvar in list(select.defined_vars.values()):
if not (dvar is filtered_variable or dvar.stinfo['relations']):
select.undefine_variable(dvar)
# global tree config: DISTINCT, LIMIT, OFFSET
@@ -305,7 +307,7 @@
# optional relation
return ovar
if all(rdef.cardinality[cardidx] in '1+'
- for rdef in rschema.rdefs.itervalues()):
+ for rdef in rschema.rdefs.values()):
# mandatory relation without any restriction on the other variable
for orel in ovar.stinfo['relations']:
if rel is orel:
@@ -670,7 +672,7 @@
insert_attr_select_relation(
select, self.filtered_variable, self.rtype, self.role,
self.target_attr, select_target_entity=False)
- values = [unicode(x) for x, in self.rqlexec(select.as_string())]
+ values = [text_type(x) for x, in self.rqlexec(select.as_string())]
except Exception:
self.exception('while computing values for %s', self)
return []
@@ -719,14 +721,14 @@
def rset_vocabulary(self, rset):
if self.i18nable:
- _ = self._cw._
+ tr = self._cw._
else:
- _ = unicode
+ tr = text_type
if self.rql_sort:
- values = [(_(label), eid) for eid, label in rset]
+ values = [(tr(label), eid) for eid, label in rset]
else:
if self.label_vid is None:
- values = [(_(label), eid) for eid, label in rset]
+ values = [(tr(label), eid) for eid, label in rset]
else:
values = [(entity.view(self.label_vid), entity.eid)
for entity in rset.entities()]
@@ -754,7 +756,7 @@
# XXX handle rel is None case in RQLPathFacet?
if self.restr_attr != 'eid':
self.select.set_distinct(True)
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
# only one value selected
if value:
self.select.add_constant_restriction(
@@ -808,7 +810,7 @@
rschema = self._cw.vreg.schema.rschema(self.rtype)
# XXX when called via ajax, no rset to compute possible types
possibletypes = self.cw_rset and self.cw_rset.column_types(0)
- for rdef in rschema.rdefs.itervalues():
+ for rdef in rschema.rdefs.values():
if possibletypes is not None:
if self.role == 'subject':
if rdef.subject not in possibletypes:
@@ -829,13 +831,13 @@
if self._cw.vreg.schema.rschema(self.rtype).final:
return False
if self.role == 'object':
- subj = utils.rqlvar_maker(defined=self.select.defined_vars,
- aliases=self.select.aliases).next()
+ subj = next(utils.rqlvar_maker(defined=self.select.defined_vars,
+ aliases=self.select.aliases))
obj = self.filtered_variable.name
else:
subj = self.filtered_variable.name
- obj = utils.rqlvar_maker(defined=self.select.defined_vars,
- aliases=self.select.aliases).next()
+ obj = next(utils.rqlvar_maker(defined=self.select.defined_vars,
+ aliases=self.select.aliases))
restrictions = []
if self.select.where:
restrictions.append(self.select.where.as_string())
@@ -916,15 +918,13 @@
def rset_vocabulary(self, rset):
if self.i18nable:
- _ = self._cw._
+ tr = self._cw._
else:
- _ = unicode
+ tr = text_type
if self.rql_sort:
- return [(_(value), value) for value, in rset]
- values = [(_(value), value) for value, in rset]
- if self.sortasc:
- return sorted(values)
- return reversed(sorted(values))
+ return [(tr(value), value) for value, in rset]
+ values = [(tr(value), value) for value, in rset]
+ return sorted(values, reverse=not self.sortasc)
class AttributeFacet(RelationAttributeFacet):
@@ -1073,7 +1073,7 @@
assert self.path and isinstance(self.path, (list, tuple)), \
'path should be a list of 3-uples, not %s' % self.path
for part in self.path:
- if isinstance(part, basestring):
+ if isinstance(part, string_types):
part = part.split()
assert len(part) == 3, \
'path should be a list of 3-uples, not %s' % part
@@ -1126,7 +1126,7 @@
cleanup_select(select, self.filtered_variable)
varmap, restrvar = self.add_path_to_select(skiplabel=True)
select.append_selected(nodes.VariableRef(restrvar))
- values = [unicode(x) for x, in self.rqlexec(select.as_string())]
+ values = [text_type(x) for x, in self.rqlexec(select.as_string())]
except Exception:
self.exception('while computing values for %s', self)
return []
@@ -1149,7 +1149,7 @@
varmap = {'X': self.filtered_variable}
actual_filter_variable = None
for part in self.path:
- if isinstance(part, basestring):
+ if isinstance(part, string_types):
part = part.split()
subject, rtype, object = part
if skiplabel and object == self.label_variable:
@@ -1165,7 +1165,7 @@
if len(attrtypes) > 1:
raise Exception('ambigous attribute %s, specify attrtype on %s'
% (rtype, self.__class__))
- self.restr_attr_type = iter(attrtypes).next()
+ self.restr_attr_type = next(iter(attrtypes))
if skipattrfilter:
actual_filter_variable = subject
continue
@@ -1253,7 +1253,7 @@
rset = self._range_rset()
if rset:
minv, maxv = rset[0]
- return [(unicode(minv), minv), (unicode(maxv), maxv)]
+ return [(text_type(minv), minv), (text_type(maxv), maxv)]
return []
def possible_values(self):
@@ -1272,7 +1272,7 @@
def formatvalue(self, value):
"""format `value` before in order to insert it in the RQL query"""
- return unicode(value)
+ return text_type(value)
def infvalue(self, min=False):
if min:
@@ -1373,7 +1373,7 @@
# *list* (see rqlexec implementation)
if rset:
minv, maxv = rset[0]
- return [(unicode(minv), minv), (unicode(maxv), maxv)]
+ return [(text_type(minv), minv), (text_type(maxv), maxv)]
return []
@@ -1392,7 +1392,7 @@
skiplabel=True, skipattrfilter=True)
restrel = None
for part in self.path:
- if isinstance(part, basestring):
+ if isinstance(part, string_types):
part = part.split()
subject, rtype, object = part
if object == self.filter_variable:
@@ -1516,7 +1516,7 @@
if not val or val & mask])
def possible_values(self):
- return [unicode(val) for label, val in self.vocabulary()]
+ return [text_type(val) for label, val in self.vocabulary()]
## html widets ################################################################
@@ -1595,7 +1595,7 @@
if selected:
cssclass += ' facetValueSelected'
w(u' \n'
- % (cssclass, xml_escape(unicode(value))))
+ % (cssclass, xml_escape(text_type(value))))
# If it is overflowed one must add padding to compensate for the vertical
# scrollbar; given current css values, 4 blanks work perfectly ...
padding = u' ' * self.scrollbar_padding_factor if overflow else u''
@@ -1754,7 +1754,7 @@
imgsrc = self._cw.data_url(self.unselected_img)
imgalt = self._cw._('not selected')
w(u' ')
+@add_metaclass(class_deprecated)
class BoxField(HTMLWidget):
"""couples label / value meant to be displayed in a box"""
- __metaclass__ = class_deprecated
__deprecation_warning__ = '[3.10] class %(cls)s is deprecated'
def __init__(self, label, value):
self.label = label
@@ -220,18 +223,19 @@
u'%s'
% (self.label, self.value))
+
+@add_metaclass(class_deprecated)
class BoxSeparator(HTMLWidget):
"""a menu separator"""
- __metaclass__ = class_deprecated
__deprecation_warning__ = '[3.10] class %(cls)s is deprecated'
def _render(self):
self.w(u'\n'
- % (cssclass, xml_escape(unicode(self.value))))
+ % (cssclass, xml_escape(text_type(self.value))))
w(u' ')
w(u' ' % (imgsrc, imgalt))
w(u''
diff -r 3a98422df969 -r 7ceb0971c694 web/form.py
--- a/web/form.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/form.py Wed Dec 16 11:23:48 2015 +0100
@@ -20,6 +20,8 @@
from warnings import warn
+from six import add_metaclass
+
from logilab.common.decorators import iclassmethod
from logilab.common.deprecation import deprecated
@@ -74,8 +76,8 @@
found
"""
+@add_metaclass(metafieldsform)
class Form(AppObject):
- __metaclass__ = metafieldsform
__registry__ = 'forms'
parent_form = None
@@ -120,7 +122,7 @@
extrakw = {}
# search for navigation parameters and customization of existing
# attributes; remaining stuff goes in extrakwargs
- for key, val in kwargs.iteritems():
+ for key, val in kwargs.items():
if key in controller.NAV_FORM_PARAMETERS:
hiddens.append( (key, val) )
elif key == 'redirect_path':
@@ -280,4 +282,3 @@
def remaining_errors(self):
return sorted(self.form_valerror.errors.items())
-
diff -r 3a98422df969 -r 7ceb0971c694 web/formfields.py
--- a/web/formfields.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/formfields.py Wed Dec 16 11:23:48 2015 +0100
@@ -66,6 +66,8 @@
from warnings import warn
from datetime import datetime, timedelta
+from six import PY2, text_type, string_types
+
from logilab.mtconverter import xml_escape
from logilab.common import nullobject
from logilab.common.date import ustrftime
@@ -159,7 +161,7 @@
:attr:`order`
key used by automatic forms to sort fields
:attr:`ignore_req_params`
- when true, this field won't consider value potentialy specified using
+ when true, this field won't consider value potentially specified using
request's form parameters (eg you won't be able to specify a value using for
instance url like http://mywebsite.com/form?field=value)
@@ -231,11 +233,14 @@
def __unicode__(self):
return self.as_string(False)
- def __str__(self):
- return self.as_string(False).encode('UTF8')
+ if PY2:
+ def __str__(self):
+ return self.as_string(False).encode('UTF8')
+ else:
+ __str__ = __unicode__
def __repr__(self):
- return self.as_string(True).encode('UTF8')
+ return self.as_string(True)
def init_widget(self, widget):
if widget is not None:
@@ -279,7 +284,7 @@
return u''
if value is True:
return u'1'
- return unicode(value)
+ return text_type(value)
def get_widget(self, form):
"""return the widget instance associated to this field"""
@@ -381,7 +386,7 @@
assert self.choices is not None
if callable(self.choices):
# pylint: disable=E1102
- if getattr(self.choices, 'im_self', None) is self:
+ if getattr(self.choices, '__self__', None) is self:
vocab = self.choices(form=form, **kwargs)
else:
vocab = self.choices(form=form, field=self, **kwargs)
@@ -508,7 +513,7 @@
class StringField(Field):
"""Use this field to edit unicode string (`String` yams type). This field
- additionaly support a `max_length` attribute that specify a maximum size for
+ additionally support a `max_length` attribute that specify a maximum size for
the string (`None` meaning no limit).
Unless explicitly specified, the widget for this field will be:
@@ -780,7 +785,7 @@
If the stream format is one of text/plain, text/html, text/rest,
text/markdown
- then a :class:`~cubicweb.web.formwidgets.TextArea` will be additionaly
+ then a :class:`~cubicweb.web.formwidgets.TextArea` will be additionally
displayed, allowing to directly the file's content when desired, instead
of choosing a file from user's file system.
"""
@@ -794,7 +799,7 @@
if data:
encoding = self.encoding(form)
try:
- form.formvalues[(self, form)] = unicode(data.getvalue(), encoding)
+ form.formvalues[(self, form)] = data.getvalue().decode(encoding)
except UnicodeError:
pass
else:
@@ -815,7 +820,7 @@
def _process_form_value(self, form):
value = form._cw.form.get(self.input_name(form))
- if isinstance(value, unicode):
+ if isinstance(value, text_type):
# file modified using a text widget
return Binary(value.encode(self.encoding(form)))
return super(EditableFileField, self)._process_form_value(form)
@@ -823,7 +828,7 @@
class BigIntField(Field):
"""Use this field to edit big integers (`BigInt` yams type). This field
- additionaly support `min` and `max` attributes that specify a minimum and/or
+ additionally support `min` and `max` attributes that specify a minimum and/or
maximum value for the integer (`None` meaning no boundary).
Unless explicitly specified, the widget for this field will be a
@@ -842,7 +847,7 @@
self.widget.attrs.setdefault('size', self.default_text_input_size)
def _ensure_correctly_typed(self, form, value):
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
value = value.strip()
if not value:
return None
@@ -907,7 +912,7 @@
class FloatField(IntField):
- """Use this field to edit floats (`Float` yams type). This field additionaly
+ """Use this field to edit floats (`Float` yams type). This field additionally
support `min` and `max` attributes as the
:class:`~cubicweb.web.formfields.IntField`.
@@ -924,7 +929,7 @@
return self.format_single_value(req, 1.234)
def _ensure_correctly_typed(self, form, value):
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
value = value.strip()
if not value:
return None
@@ -946,7 +951,7 @@
def format_single_value(self, req, value):
if value:
value = format_time(value.days * 24 * 3600 + value.seconds)
- return unicode(value)
+ return text_type(value)
return u''
def example_format(self, req):
@@ -956,7 +961,7 @@
return u'20s, 10min, 24h, 4d'
def _ensure_correctly_typed(self, form, value):
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
value = value.strip()
if not value:
return None
@@ -986,14 +991,14 @@
return self.format_single_value(req, datetime.now())
def _ensure_correctly_typed(self, form, value):
- if isinstance(value, basestring):
+ if isinstance(value, string_types):
value = value.strip()
if not value:
return None
try:
value = form._cw.parse_datetime(value, self.etype)
except ValueError as ex:
- raise ProcessFormError(unicode(ex))
+ raise ProcessFormError(text_type(ex))
return value
@@ -1083,7 +1088,7 @@
linkedto = form.linked_to.get((self.name, self.role))
if linkedto:
buildent = form._cw.entity_from_eid
- return [(buildent(eid).view('combobox'), unicode(eid))
+ return [(buildent(eid).view('combobox'), text_type(eid))
for eid in linkedto]
return []
@@ -1095,7 +1100,7 @@
# vocabulary doesn't include current values, add them
if form.edited_entity.has_eid():
rset = form.edited_entity.related(self.name, self.role)
- vocab += [(e.view('combobox'), unicode(e.eid))
+ vocab += [(e.view('combobox'), text_type(e.eid))
for e in rset.entities()]
return vocab
@@ -1129,11 +1134,11 @@
if entity.eid in done:
continue
done.add(entity.eid)
- res.append((entity.view('combobox'), unicode(entity.eid)))
+ res.append((entity.view('combobox'), text_type(entity.eid)))
return res
def format_single_value(self, req, value):
- return unicode(value)
+ return text_type(value)
def process_form_value(self, form):
"""process posted form and return correctly typed value"""
diff -r 3a98422df969 -r 7ceb0971c694 web/formwidgets.py
--- a/web/formwidgets.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/formwidgets.py Wed Dec 16 11:23:48 2015 +0100
@@ -99,6 +99,8 @@
from datetime import date
from warnings import warn
+from six import text_type, string_types
+
from logilab.mtconverter import xml_escape
from logilab.common.deprecation import deprecated
from logilab.common.date import todatetime
@@ -282,7 +284,7 @@
"""
posted = form._cw.form
val = posted.get(field.input_name(form, self.suffix))
- if isinstance(val, basestring):
+ if isinstance(val, string_types):
val = val.strip()
return val
@@ -416,7 +418,7 @@
lines = value.splitlines()
linecount = len(lines)
for line in lines:
- linecount += len(line) / self._columns
+ linecount += len(line) // self._columns
attrs.setdefault('cols', self._columns)
attrs.setdefault('rows', min(self._maxrows, linecount + self._minrows))
return tags.textarea(value, name=field.input_name(form, self.suffix),
@@ -474,7 +476,7 @@
options.append(u'')
if not 'size' in attrs:
if self._multiple:
- size = unicode(min(self.default_size, len(vocab) or 1))
+ size = text_type(min(self.default_size, len(vocab) or 1))
else:
size = u'1'
attrs['size'] = size
@@ -616,7 +618,7 @@
iattrs['checked'] = u'checked'
tag = tags.input(name=field.input_name(form, self.suffix),
type=self.type, value=value, **iattrs)
- options.append(u'%s %s' % (tag, label))
+ options.append(u'' % (tag, xml_escape(label)))
return sep.join(options)
@@ -706,7 +708,7 @@
else:
value = self.value
attrs = self.attributes(form, field)
- attrs.setdefault('size', unicode(self.default_size))
+ attrs.setdefault('size', text_type(self.default_size))
return tags.input(name=field.input_name(form, self.suffix),
value=value, type='text', **attrs)
@@ -779,13 +781,13 @@
try:
date = todatetime(req.parse_datetime(datestr, 'Date'))
except ValueError as exc:
- raise ProcessFormError(unicode(exc))
+ raise ProcessFormError(text_type(exc))
if timestr is None:
return date
try:
time = req.parse_datetime(timestr, 'Time')
except ValueError as exc:
- raise ProcessFormError(unicode(exc))
+ raise ProcessFormError(text_type(exc))
return date.replace(hour=time.hour, minute=time.minute, second=time.second)
@@ -993,12 +995,12 @@
req = form._cw
values = {}
path = req.form.get(field.input_name(form, 'path'))
- if isinstance(path, basestring):
+ if isinstance(path, string_types):
path = path.strip()
if path is None:
path = u''
fqs = req.form.get(field.input_name(form, 'fqs'))
- if isinstance(fqs, basestring):
+ if isinstance(fqs, string_types):
fqs = fqs.strip() or None
if fqs:
for i, line in enumerate(fqs.split('\n')):
@@ -1009,7 +1011,7 @@
except ValueError:
raise ProcessFormError(req._("wrong query parameter line %s") % (i+1))
# value will be url quoted by build_url_params
- values.setdefault(key.encode(req.encoding), []).append(val)
+ values.setdefault(key, []).append(val)
if not values:
return path
return u'%s?%s' % (path, req.build_url_params(**values))
@@ -1094,4 +1096,3 @@
'%(label)s' % {
'label': label, 'imgsrc': imgsrc,
'domid': self.domid, 'href': self.href}
-
diff -r 3a98422df969 -r 7ceb0971c694 web/htmlwidgets.py
--- a/web/htmlwidgets.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/htmlwidgets.py Wed Dec 16 11:23:48 2015 +0100
@@ -24,6 +24,9 @@
import random
from math import floor
+from six import add_metaclass
+from six.moves import range
+
from logilab.mtconverter import xml_escape
from logilab.common.deprecation import class_deprecated
@@ -115,9 +118,9 @@
self.w(u' ')
+@add_metaclass(class_deprecated)
class SideBoxWidget(BoxWidget):
"""default CubicWeb's sidebox widget"""
- __metaclass__ = class_deprecated
__deprecation_warning__ = '[3.10] class %(cls)s is deprecated'
title_class = u'sideBoxTitle'
@@ -207,9 +210,9 @@
self.w(u'
%s | ' % (' '.join(attrs), column.name or u''))
self.w(u'' % (' '.join(attrs)))
for cellvid, colindex in column.cellrenderers:
self.model.render_cell(cellvid, rowindex, colindex, w=self.w)
@@ -361,5 +365,3 @@
def itercols(self, rowindex):
for column in self.columns:
yield column, self.model.sortvalue(rowindex, column.rset_sortcol)
-
-
diff -r 3a98422df969 -r 7ceb0971c694 web/http_headers.py
--- a/web/http_headers.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/http_headers.py Wed Dec 16 11:23:48 2015 +0100
@@ -2,11 +2,14 @@
# http://twistedmatrix.com/trac/wiki/TwistedWeb2
-import types, time
+import time
from calendar import timegm
import base64
import re
-import urlparse
+
+from six import string_types
+from six.moves.urllib.parse import urlparse
+
def dashCapitalize(s):
''' Capitalize a string, making sure to treat - as a word seperator '''
@@ -295,9 +298,9 @@
cur = cur+1
if qpair:
- raise ValueError, "Missing character after '\\'"
+ raise ValueError("Missing character after '\\'")
if quoted:
- raise ValueError, "Missing end quote"
+ raise ValueError("Missing end quote")
if start != cur:
if foldCase:
@@ -347,7 +350,7 @@
##### parser utilities:
def checkSingleToken(tokens):
if len(tokens) != 1:
- raise ValueError, "Expected single token, not %s." % (tokens,)
+ raise ValueError("Expected single token, not %s." % (tokens,))
return tokens[0]
def parseKeyValue(val):
@@ -355,11 +358,11 @@
return val[0], None
elif len(val) == 3 and val[1] == Token('='):
return val[0], val[2]
- raise ValueError, "Expected key or key=value, but got %s." % (val,)
+ raise ValueError("Expected key or key=value, but got %s." % (val,))
def parseArgs(field):
args = split(field, Token(';'))
- val = args.next()
+ val = next(args)
args = [parseKeyValue(arg) for arg in args]
return val, args
@@ -380,7 +383,7 @@
def unique(seq):
'''if seq is not a string, check it's a sequence of one element and return it'''
- if isinstance(seq, basestring):
+ if isinstance(seq, string_types):
return seq
if len(seq) != 1:
raise ValueError('single value required, not %s' % seq)
@@ -398,7 +401,7 @@
"""Ensure origin is a valid URL-base stuff, or null"""
if origin == 'null':
return origin
- p = urlparse.urlparse(origin)
+ p = urlparse(origin)
if p.params or p.query or p.username or p.path not in ('', '/'):
raise ValueError('Incorrect Accept-Control-Allow-Origin value %s' % origin)
if p.scheme not in ('http', 'https'):
@@ -452,14 +455,15 @@
"""
if (value in (True, 1) or
- isinstance(value, basestring) and value.lower() == 'true'):
+ isinstance(value, string_types) and value.lower() == 'true'):
return 'true'
if (value in (False, 0) or
- isinstance(value, basestring) and value.lower() == 'false'):
+ isinstance(value, string_types) and value.lower() == 'false'):
return 'false'
raise ValueError("Invalid true/false header value: %s" % value)
class MimeType(object):
+ @classmethod
def fromString(klass, mimeTypeString):
"""Generate a MimeType object from the given string.
@@ -469,8 +473,6 @@
"""
return DefaultHTTPHandler.parse('content-type', [mimeTypeString])
- fromString = classmethod(fromString)
-
def __init__(self, mediaType, mediaSubtype, params={}, **kwargs):
"""
@type mediaType: C{str}
@@ -499,14 +501,14 @@
return "MimeType(%r, %r, %r)" % (self.mediaType, self.mediaSubtype, self.params)
def __hash__(self):
- return hash(self.mediaType)^hash(self.mediaSubtype)^hash(tuple(self.params.iteritems()))
+ return hash(self.mediaType)^hash(self.mediaSubtype)^hash(tuple(self.params.items()))
##### Specific header parsers.
def parseAccept(field):
type, args = parseArgs(field)
if len(type) != 3 or type[1] != Token('/'):
- raise ValueError, "MIME Type "+str(type)+" invalid."
+ raise ValueError("MIME Type "+str(type)+" invalid.")
# okay, this spec is screwy. A 'q' parameter is used as the separator
# between MIME parameters and (as yet undefined) additional HTTP
@@ -569,7 +571,7 @@
type, args = parseArgs(header)
if len(type) != 3 or type[1] != Token('/'):
- raise ValueError, "MIME Type "+str(type)+" invalid."
+ raise ValueError("MIME Type "+str(type)+" invalid.")
args = [(kv[0].lower(), kv[1]) for kv in args]
@@ -730,7 +732,7 @@
out ="%s/%s"%(mimeType.mediaType, mimeType.mediaSubtype)
if mimeType.params:
- out+=';'+generateKeyValues(mimeType.params.iteritems())
+ out+=';'+generateKeyValues(mimeType.params.items())
if q != 1.0:
out+=(';q=%.3f' % (q,)).rstrip('0').rstrip('.')
@@ -766,7 +768,8 @@
v = [field.strip().lower() for field in v.split(',')]
return k, v
-def generateCacheControl((k, v)):
+def generateCacheControl(args):
+ k, v = args
if v is None:
return str(k)
else:
@@ -833,7 +836,7 @@
def generateContentType(mimeType):
out = "%s/%s" % (mimeType.mediaType, mimeType.mediaSubtype)
if mimeType.params:
- out += ';' + generateKeyValues(mimeType.params.iteritems())
+ out += ';' + generateKeyValues(mimeType.params.items())
return out
def generateIfRange(dateOrETag):
@@ -854,7 +857,7 @@
try:
l = []
- for k, v in dict(challenge).iteritems():
+ for k, v in dict(challenge).items():
l.append("%s=%s" % (k, quoteString(v)))
_generated.append("%s %s" % (scheme, ", ".join(l)))
@@ -864,7 +867,7 @@
return _generated
def generateAuthorization(seq):
- return [' '.join(seq)]
+ return [' '.join(str(v) for v in seq)]
####
@@ -1326,10 +1329,10 @@
self._headers = {}
self.handler = handler
if headers is not None:
- for key, value in headers.iteritems():
+ for key, value in headers.items():
self.setHeader(key, value)
if rawHeaders is not None:
- for key, value in rawHeaders.iteritems():
+ for key, value in rawHeaders.items():
self.setRawHeaders(key, value)
def _setRawHeaders(self, headers):
@@ -1458,7 +1461,7 @@
"""Return an iterator of key, value pairs of all headers
contained in this object, as strings. The keys are capitalized
in canonical capitalization."""
- for k, v in self._raw_headers.iteritems():
+ for k, v in self._raw_headers.items():
if v is _RecalcNeeded:
v = self._toRaw(k)
yield self.canonicalNameCaps(k), v
@@ -1480,7 +1483,7 @@
is strictly an error, but we're nice.).
"""
-iteritems = lambda x: x.iteritems()
+iteritems = lambda x: x.items()
parser_general_headers = {
diff -r 3a98422df969 -r 7ceb0971c694 web/httpcache.py
--- a/web/httpcache.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/httpcache.py Wed Dec 16 11:23:48 2015 +0100
@@ -31,6 +31,7 @@
def set_headers(self):
self.req.set_header('Cache-control', 'no-cache')
+ self.req.set_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
class MaxAgeHTTPCacheManager(NoHTTPCacheManager):
@@ -68,7 +69,7 @@
try:
req.set_header('Etag', '"%s"' % self.etag())
except NoEtag:
- self.req.set_header('Cache-control', 'no-cache')
+ super(EtagHTTPCacheManager, self).set_headers()
return
req.set_header('Cache-control',
'must-revalidate,max-age=%s' % self.max_age())
@@ -178,4 +179,3 @@
('if-none-match', if_none_match),
#('if-modified-since', if_modified_since),
]
-
diff -r 3a98422df969 -r 7ceb0971c694 web/propertysheet.py
--- a/web/propertysheet.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/propertysheet.py Wed Dec 16 11:23:48 2015 +0100
@@ -22,6 +22,8 @@
import re
import os
import os.path as osp
+import tempfile
+
TYPE_CHECKS = [('STYLESHEETS', list), ('JAVASCRIPTS', list),
('STYLESHEETS_IE', list), ('STYLESHEETS_PRINT', list),
@@ -51,13 +53,13 @@
self.clear()
self._ordered_propfiles = []
self._propfile_mtime = {}
- self._sourcefile_mtime = {}
- self._cache = {}
def load(self, fpath):
scriptglobals = self.context.copy()
scriptglobals['__file__'] = fpath
- execfile(fpath, scriptglobals, self)
+ with open(fpath, 'rb') as fobj:
+ code = compile(fobj.read(), fpath, 'exec')
+ exec(code, scriptglobals, self)
for name, type in TYPE_CHECKS:
if name in self:
if not isinstance(self[name], type):
@@ -67,10 +69,7 @@
self._ordered_propfiles.append(fpath)
def need_reload(self):
- for rid, (adirectory, rdirectory, mtime) in self._cache.items():
- if os.stat(osp.join(rdirectory, rid)).st_mtime > mtime:
- del self._cache[rid]
- for fpath, mtime in self._propfile_mtime.iteritems():
+ for fpath, mtime in self._propfile_mtime.items():
if os.stat(fpath).st_mtime > mtime:
return True
return False
@@ -86,31 +85,29 @@
self.reload()
def process_resource(self, rdirectory, rid):
+ cachefile = osp.join(self._cache_directory, rid)
+ self.debug('processing %s/%s into %s',
+ rdirectory, rid, cachefile)
+ rcachedir = osp.dirname(cachefile)
+ if not osp.exists(rcachedir):
+ os.makedirs(rcachedir)
+ sourcefile = osp.join(rdirectory, rid)
+ with open(sourcefile) as f:
+ content = f.read()
+ # XXX replace % not followed by a paren by %% to avoid having to do
+ # this in the source css file ?
try:
- return self._cache[rid][0]
- except KeyError:
- cachefile = osp.join(self._cache_directory, rid)
- self.debug('caching processed %s/%s into %s',
- rdirectory, rid, cachefile)
- rcachedir = osp.dirname(cachefile)
- if not osp.exists(rcachedir):
- os.makedirs(rcachedir)
- sourcefile = osp.join(rdirectory, rid)
- content = file(sourcefile).read()
- # XXX replace % not followed by a paren by %% to avoid having to do
- # this in the source css file ?
- try:
- content = self.compile(content)
- except ValueError as ex:
- self.error("can't process %s/%s: %s", rdirectory, rid, ex)
- adirectory = rdirectory
- else:
- stream = file(cachefile, 'w')
+ content = self.compile(content)
+ except ValueError as ex:
+ self.error("can't process %s/%s: %s", rdirectory, rid, ex)
+ adirectory = rdirectory
+ else:
+ tmpfd, tmpfile = tempfile.mkstemp(dir=rcachedir, prefix=osp.basename(cachefile))
+ with os.fdopen(tmpfd, 'w') as stream:
stream.write(content)
- stream.close()
- adirectory = self._cache_directory
- self._cache[rid] = (adirectory, rdirectory, os.stat(sourcefile).st_mtime)
- return adirectory
+ os.rename(tmpfile, cachefile)
+ adirectory = self._cache_directory
+ return adirectory
def compile(self, content):
return self._percent_rgx.sub('%%', content) % self
diff -r 3a98422df969 -r 7ceb0971c694 web/request.py
--- a/web/request.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/request.py Wed Dec 16 11:23:48 2015 +0100
@@ -22,15 +22,16 @@
import time
import random
import base64
-import urllib
-from StringIO import StringIO
from hashlib import sha1 # pylint: disable=E0611
-from Cookie import SimpleCookie
from calendar import timegm
from datetime import date, datetime
-from urlparse import urlsplit
-import httplib
from warnings import warn
+from io import BytesIO
+
+from six import PY2, binary_type, text_type, string_types
+from six.moves import http_client
+from six.moves.urllib.parse import urlsplit, quote as urlquote
+from six.moves.http_cookies import SimpleCookie
from rql.utils import rqlvar_maker
@@ -41,7 +42,7 @@
from cubicweb import AuthenticationError
from cubicweb.req import RequestSessionBase
from cubicweb.uilib import remove_html_tags, js
-from cubicweb.utils import SizeConstrainedList, HTMLHead, make_uid
+from cubicweb.utils import HTMLHead, make_uid
from cubicweb.view import TRANSITIONAL_DOCTYPE_NOEXT
from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
RequestError, StatusResponse)
@@ -51,7 +52,7 @@
_MARKER = object()
def build_cb_uid(seed):
- sha = sha1('%s%s%s' % (time.time(), seed, random.random()))
+ sha = sha1(('%s%s%s' % (time.time(), seed, random.random())).encode('ascii'))
return 'cb_%s' % (sha.hexdigest())
@@ -137,12 +138,12 @@
#: received headers
self._headers_in = Headers()
if headers is not None:
- for k, v in headers.iteritems():
+ for k, v in headers.items():
self._headers_in.addRawHeader(k, v)
#: form parameters
self.setup_params(form)
#: received body
- self.content = StringIO()
+ self.content = BytesIO()
# prepare output header
#: Header used for the final response
self.headers_out = Headers()
@@ -242,7 +243,7 @@
'__redirectvid', '__redirectrql'))
def setup_params(self, params):
- """WARNING: we're intentionaly leaving INTERNAL_FIELD_VALUE here
+ """WARNING: we're intentionally leaving INTERNAL_FIELD_VALUE here
subclasses should overrides to
"""
@@ -250,12 +251,13 @@
if params is None:
return
encoding = self.encoding
- for param, val in params.iteritems():
+ for param, val in params.items():
if isinstance(val, (tuple, list)):
- val = [unicode(x, encoding) for x in val]
+ if PY2:
+ val = [unicode(x, encoding) for x in val]
if len(val) == 1:
val = val[0]
- elif isinstance(val, str):
+ elif PY2 and isinstance(val, str):
val = unicode(val, encoding)
if param in self.no_script_form_params and val:
val = self.no_script_form_param(param, val)
@@ -317,7 +319,7 @@
return None
def set_message(self, msg):
- assert isinstance(msg, unicode)
+ assert isinstance(msg, text_type)
self.reset_message()
self._msg = msg
@@ -330,7 +332,7 @@
def set_redirect_message(self, msg):
# TODO - this should probably be merged with append_to_redirect_message
- assert isinstance(msg, unicode)
+ assert isinstance(msg, text_type)
msgid = self.redirect_message_id()
self.session.data[msgid] = msg
return msgid
@@ -396,26 +398,6 @@
return False
return True
- def update_breadcrumbs(self):
- """stores the last visisted page in session data"""
- searchstate = self.search_state[0]
- if searchstate == 'normal':
- breadcrumbs = self.session.data.get('breadcrumbs')
- if breadcrumbs is None:
- breadcrumbs = SizeConstrainedList(10)
- self.session.data['breadcrumbs'] = breadcrumbs
- breadcrumbs.append(self.url())
- else:
- url = self.url()
- if breadcrumbs and breadcrumbs[-1] != url:
- breadcrumbs.append(url)
-
- def last_visited_page(self):
- breadcrumbs = self.session.data.get('breadcrumbs')
- if breadcrumbs:
- return breadcrumbs.pop()
- return self.base_url()
-
# web edition helpers #####################################################
@cached # so it's writed only once
@@ -437,7 +419,7 @@
eids = form['eid']
except KeyError:
raise NothingToEdit(self._('no selected entities'))
- if isinstance(eids, basestring):
+ if isinstance(eids, string_types):
eids = (eids,)
for peid in eids:
if withtype:
@@ -569,18 +551,18 @@
header = [disposition]
unicode_filename = None
try:
- ascii_filename = filename.encode('ascii')
+ ascii_filename = filename.encode('ascii').decode('ascii')
except UnicodeEncodeError:
# fallback filename for very old browser
unicode_filename = filename
- ascii_filename = filename.encode('ascii', 'ignore')
+ ascii_filename = filename.encode('ascii', 'ignore').decode('ascii')
# escape " and \
# see http://greenbytes.de/tech/tc2231/#attwithfilenameandextparamescaped
ascii_filename = ascii_filename.replace('\x5c', r'\\').replace('"', r'\"')
header.append('filename="%s"' % ascii_filename)
if unicode_filename is not None:
# encoded filename according RFC5987
- urlquoted_filename = urllib.quote(unicode_filename.encode('utf-8'), '')
+ urlquoted_filename = urlquote(unicode_filename.encode('utf-8'), '')
header.append("filename*=utf-8''" + urlquoted_filename)
self.set_header('content-disposition', ';'.join(header))
@@ -596,7 +578,7 @@
:param localfile: if True, the default data dir prefix is added to the
JS filename
"""
- if isinstance(jsfiles, basestring):
+ if isinstance(jsfiles, string_types):
jsfiles = (jsfiles,)
for jsfile in jsfiles:
if localfile:
@@ -616,7 +598,7 @@
the css inclusion. cf:
http://msdn.microsoft.com/en-us/library/ms537512(VS.85).aspx
"""
- if isinstance(cssfiles, basestring):
+ if isinstance(cssfiles, string_types):
cssfiles = (cssfiles,)
if ieonly:
if self.ie_browser():
@@ -702,7 +684,7 @@
return urlsplit(self.base_url())[2]
def data_url(self, relpath):
- """returns the absolute path for a data resouce"""
+ """returns the absolute path for a data resource"""
return self.datadir_url + relpath
@cached
@@ -722,25 +704,19 @@
Some response cache headers may be set by this method.
"""
modified = True
- if self.get_header('Cache-Control') not in ('max-age=0', 'no-cache'):
- # Here, we search for any invalid 'not modified' condition
- # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3
- validators = get_validators(self._headers_in)
- if validators: # if we have no
- modified = any(func(val, self.headers_out) for func, val in validators)
+ # Here, we search for any invalid 'not modified' condition
+ # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3
+ validators = get_validators(self._headers_in)
+ if validators: # if we have no
+ modified = any(func(val, self.headers_out) for func, val in validators)
# Forge expected response
- if modified:
- if 'Expires' not in self.headers_out:
- # Expires header seems to be required by IE7 -- Are you sure ?
- self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
- # /!\ no raise, the function returns and we keep processing the request
- else:
+ if not modified:
# overwrite headers_out to forge a brand new not-modified response
self.headers_out = self._forge_cached_headers()
if self.http_method() in ('HEAD', 'GET'):
- self.status_out = httplib.NOT_MODIFIED
+ self.status_out = http_client.NOT_MODIFIED
else:
- self.status_out = httplib.PRECONDITION_FAILED
+ self.status_out = http_client.PRECONDITION_FAILED
# XXX replace by True once validate_cache bw compat method is dropped
return self.status_out
# XXX replace by False once validate_cache bw compat method is dropped
@@ -800,7 +776,7 @@
def header_accept_language(self):
"""returns an ordered list of preferred languages"""
acceptedlangs = self.get_header('Accept-Language', raw=False) or {}
- for lang, _ in sorted(acceptedlangs.iteritems(), key=lambda x: x[1],
+ for lang, _ in sorted(acceptedlangs.items(), key=lambda x: x[1],
reverse=True):
lang = lang.split('-')[0]
yield lang
@@ -844,7 +820,7 @@
scheme = scheme.lower()
try:
assert scheme == "basic"
- user, passwd = base64.decodestring(rest).split(":", 1)
+ user, passwd = base64.decodestring(rest.encode('ascii')).split(b":", 1)
# XXX HTTP header encoding: use email.Header?
return user.decode('UTF8'), passwd
except Exception as ex:
@@ -966,8 +942,10 @@
def __getattribute__(self, attr):
raise AuthenticationError()
- def __nonzero__(self):
+ def __bool__(self):
return False
+
+ __nonzero__ = __bool__
class _MockAnonymousSession(object):
sessionid = 'thisisnotarealsession'
@@ -1023,8 +1001,8 @@
self.set_language(lang)
except KeyError:
# this occurs usually during test execution
- self._ = self.__ = unicode
- self.pgettext = lambda x, y: unicode(y)
+ self._ = self.__ = text_type
+ self.pgettext = lambda x, y: text_type(y)
entity_metas = _cnx_func('entity_metas')
source_defs = _cnx_func('source_defs')
diff -r 3a98422df969 -r 7ceb0971c694 web/schemaviewer.py
--- a/web/schemaviewer.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/schemaviewer.py Wed Dec 16 11:23:48 2015 +0100
@@ -18,7 +18,9 @@
"""an helper class to display CubicWeb schema using ureports"""
__docformat__ = "restructuredtext en"
-_ = unicode
+from cubicweb import _
+
+from six import string_types
from logilab.common.ureports import Section, Title, Table, Link, Span, Text
@@ -218,7 +220,7 @@
elif prop == 'constraints':
val = ', '.join([c.expression for c in val])
elif isinstance(val, dict):
- for key, value in val.iteritems():
+ for key, value in val.items():
if isinstance(value, (list, tuple)):
val[key] = ', '.join(sorted( str(v) for v in value))
val = str(val)
@@ -226,7 +228,7 @@
elif isinstance(val, (list, tuple)):
val = sorted(val)
val = ', '.join(str(v) for v in val)
- elif val and isinstance(val, basestring):
+ elif val and isinstance(val, string_types):
val = _(val)
else:
val = str(val)
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/schema.py
--- a/web/test/data/schema.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/data/schema.py Wed Dec 16 11:23:48 2015 +0100
@@ -20,6 +20,9 @@
String, Int, Datetime, Boolean, Float)
from yams.constraints import IntervalBoundConstraint
+from cubicweb import _
+
+
class Salesterm(EntityType):
described_by_test = SubjectRelation('File', cardinality='1*',
composite='subject', inlined=True)
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/static/jstests/ajax_url0.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/static/jstests/ajax_url0.html Wed Dec 16 11:23:48 2015 +0100
@@ -0,0 +1,3 @@
+ |
+
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/static/jstests/ajax_url1.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/static/jstests/ajax_url1.html Wed Dec 16 11:23:48 2015 +0100
@@ -0,0 +1,6 @@
+Hello+
+
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/static/jstests/ajaxresult.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/static/jstests/ajaxresult.json Wed Dec 16 11:23:48 2015 +0100
@@ -0,0 +1,1 @@
+["foo", "bar"]
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/static/jstests/test_ajax.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/static/jstests/test_ajax.html Wed Dec 16 11:23:48 2015 +0100
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hello+cubicweb.ajax.js functions tests+ +
has been removed
+ assert.equal($('#qunit-fixture').children().length, 1);
+ assert.equal($('div.ajaxHtmlHead').length, 0);
+ assert.equal($('#qunit-fixture h1').html(), 'Hello');
+ } finally {
+ done();
+ };
+ }
+ );
+ });
+
+ QUnit.test('test addCallback', function (assert) {
+ assert.expect(3);
+ assert.equal($('#qunit-fixture').children().length, 0);
+ var done = assert.async();
+ var d = $('#qunit-fixture').loadxhtml('static/jstests/ajax_url0.html', null, 'GET');
+ d.addCallback(function() {
+ try {
+ assert.equal($('#qunit-fixture').children().length, 1);
+ assert.equal($('#qunit-fixture h1').html(), 'Hello');
+ } finally {
+ done();
+ };
+ });
+ });
+
+ QUnit.test('test callback after synchronous request', function (assert) {
+ assert.expect(1);
+ var deferred = new Deferred();
+ var result = jQuery.ajax({
+ url: 'static/jstests/ajax_url0.html',
+ async: false,
+ beforeSend: function(xhr) {
+ deferred._req = xhr;
+ },
+ success: function(data, status) {
+ deferred.success(data);
+ }
+ });
+ var done = assert.async();
+ deferred.addCallback(function() {
+ try {
+ // add an assertion to ensure the callback is executed
+ assert.ok(true, "callback is executed");
+ } finally {
+ done();
+ };
+ });
+ });
+
+ QUnit.test('test addCallback with parameters', function (assert) {
+ assert.expect(3);
+ assert.equal($('#qunit-fixture').children().length, 0);
+ var done = assert.async();
+ var d = $('#qunit-fixture').loadxhtml('static/jstests/ajax_url0.html', null, 'GET');
+ d.addCallback(function(data, req, arg1, arg2) {
+ try {
+ assert.equal(arg1, 'Hello');
+ assert.equal(arg2, 'world');
+ } finally {
+ done();
+ };
+ },
+ 'Hello', 'world');
+ });
+
+ QUnit.test('test callback after synchronous request with parameters', function (assert) {
+ assert.expect(3);
+ var deferred = new Deferred();
+ deferred.addCallback(function(data, req, arg1, arg2) {
+ // add an assertion to ensure the callback is executed
+ try {
+ assert.ok(true, "callback is executed");
+ assert.equal(arg1, 'Hello');
+ assert.equal(arg2, 'world');
+ } finally {
+ done();
+ };
+ },
+ 'Hello', 'world');
+ deferred.addErrback(function() {
+ // throw an exception to start errback chain
+ try {
+ throw this._error;
+ } finally {
+ done();
+ };
+ });
+ var done = assert.async();
+ var result = jQuery.ajax({
+ url: 'static/jstests/ajax_url0.html',
+ async: false,
+ beforeSend: function(xhr) {
+ deferred._req = xhr;
+ },
+ success: function(data, status) {
+ deferred.success(data);
+ }
+ });
+ });
+
+ QUnit.test('test addErrback', function (assert) {
+ assert.expect(1);
+ var done = assert.async();
+ var d = $('#qunit-fixture').loadxhtml('static/jstests/nonexistent.html', null, 'GET');
+ d.addCallback(function() {
+ // should not be executed
+ assert.ok(false, "callback is executed");
+ });
+ d.addErrback(function() {
+ try {
+ assert.ok(true, "errback is executed");
+ } finally {
+ done();
+ };
+ });
+ });
+
+ QUnit.test('test callback execution order', function (assert) {
+ assert.expect(3);
+ var counter = 0;
+ var done = assert.async();
+ var d = $('#qunit-fixture').loadxhtml('static/jstests/ajax_url0.html', null, 'GET');
+ d.addCallback(function() {
+ assert.equal(++counter, 1); // should be executed first
+ });
+ d.addCallback(function() {
+ assert.equal(++counter, 2);
+ });
+ d.addCallback(function() {
+ try {
+ assert.equal(++counter, 3);
+ } finally {
+ done();
+ }
+ });
+ });
+
+ QUnit.test('test already included resources are ignored (ajax_url1.html)', function (assert) {
+ assert.expect(10);
+ var scriptsIncluded = jsSources();
+ // NOTE:
+ assert.equal(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
+ assert.equal($('head link').length, 1);
+ /* use endswith because in pytest context we have an absolute path */
+ assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
+ var done = assert.async();
+ $('#qunit-fixture').loadxhtml('static/jstests/ajax_url1.html', null, 'GET')
+ .addCallback(function() {
+ var origLength = scriptsIncluded.length;
+ scriptsIncluded = jsSources();
+ try {
+ // check that foo.js has been inserted in
+ assert.equal(scriptsIncluded.length, origLength + 1);
+ assert.equal(scriptsIncluded.indexOf('http://foo.js'), 0);
+ // check that has been removed
+ assert.equal($('#qunit-fixture').children().length, 1);
+ assert.equal($('div.ajaxHtmlHead').length, 0);
+ assert.equal($('#qunit-fixture h1').html(), 'Hello');
+ // qunit.css is not added twice
+ assert.equal($('head link').length, 1);
+ /* use endswith because in pytest context we have an absolute path */
+ assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
+ } finally {
+ done();
+ }
+ }
+ );
+ });
+
+ QUnit.test('test synchronous request loadRemote', function (assert) {
+ var res = loadRemote('static/jstests/ajaxresult.json', {},
+ 'GET', true);
+ assert.deepEqual(res, ['foo', 'bar']);
+ });
+
+ QUnit.test('test event on CubicWeb', function (assert) {
+ assert.expect(1);
+ var done = assert.async();
+ var events = null;
+ $(CubicWeb).bind('server-response', function() {
+ // check that server-response event on CubicWeb is triggered
+ events = 'CubicWeb';
+ });
+ $('#qunit-fixture').loadxhtml('static/jstests/ajax_url0.html', null, 'GET')
+ .addCallback(function() {
+ try {
+ assert.equal(events, 'CubicWeb');
+ } finally {
+ done();
+ };
+ }
+ );
+ });
+
+ QUnit.test('test event on node', function (assert) {
+ assert.expect(3);
+ var done = assert.async();
+ var nodes = [];
+ $('#qunit-fixture').bind('server-response', function() {
+ nodes.push('node');
+ });
+ $(CubicWeb).bind('server-response', function() {
+ nodes.push('CubicWeb');
+ });
+ $('#qunit-fixture').loadxhtml('static/jstests/ajax_url0.html', null, 'GET')
+ .addCallback(function() {
+ try {
+ assert.equal(nodes.length, 2);
+ // check that server-response event on CubicWeb is triggered
+ // only once and event server-response on node is triggered
+ assert.equal(nodes[0], 'CubicWeb');
+ assert.equal(nodes[1], 'node');
+ } finally {
+ done();
+ };
+ }
+ );
+ });
+});
+
diff -r 3a98422df969 -r 7ceb0971c694 web/test/data/static/jstests/test_htmlhelpers.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/data/static/jstests/test_htmlhelpers.html Wed Dec 16 11:23:48 2015 +0100
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 3a98422df969 -r 7ceb0971c694 web/test/jstests/ajax_url1.html
--- a/web/test/jstests/ajax_url1.html Tue Dec 15 19:05:41 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-cubicweb.htmlhelpers.js functions tests+ +
cw.utils functions tests+ +
Hello-
-
diff -r 3a98422df969 -r 7ceb0971c694 web/test/jstests/ajax_url2.html
--- a/web/test/jstests/ajax_url2.html Tue Dec 15 19:05:41 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-
-
- Hello-
-
diff -r 3a98422df969 -r 7ceb0971c694 web/test/jstests/ajaxresult.json
--- a/web/test/jstests/ajaxresult.json Tue Dec 15 19:05:41 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-["foo", "bar"]
diff -r 3a98422df969 -r 7ceb0971c694 web/test/jstests/test_ajax.html
--- a/web/test/jstests/test_ajax.html Tue Dec 15 19:05:41 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Hello-cubicweb.ajax.js functions tests- -
has been removed
- assert.equal($('#qunit-fixture').children().length, 1);
- assert.equal($('div.ajaxHtmlHead').length, 0);
- assert.equal($('#qunit-fixture h1').html(), 'Hello');
- } finally {
- done();
- };
- }
- );
- });
-
- QUnit.test('test addCallback', function (assert) {
- assert.expect(3);
- assert.equal($('#qunit-fixture').children().length, 0);
- var done = assert.async();
- var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
- d.addCallback(function() {
- try {
- assert.equal($('#qunit-fixture').children().length, 1);
- assert.equal($('#qunit-fixture h1').html(), 'Hello');
- } finally {
- done();
- };
- });
- });
-
- QUnit.test('test callback after synchronous request', function (assert) {
- assert.expect(1);
- var deferred = new Deferred();
- var result = jQuery.ajax({
- url: BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html',
- async: false,
- beforeSend: function(xhr) {
- deferred._req = xhr;
- },
- success: function(data, status) {
- deferred.success(data);
- }
- });
- var done = assert.async();
- deferred.addCallback(function() {
- try {
- // add an assertion to ensure the callback is executed
- assert.ok(true, "callback is executed");
- } finally {
- done();
- };
- });
- });
-
- QUnit.test('test addCallback with parameters', function (assert) {
- assert.expect(3);
- assert.equal($('#qunit-fixture').children().length, 0);
- var done = assert.async();
- var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
- d.addCallback(function(data, req, arg1, arg2) {
- try {
- assert.equal(arg1, 'Hello');
- assert.equal(arg2, 'world');
- } finally {
- done();
- };
- },
- 'Hello', 'world');
- });
-
- QUnit.test('test callback after synchronous request with parameters', function (assert) {
- assert.expect(3);
- var deferred = new Deferred();
- deferred.addCallback(function(data, req, arg1, arg2) {
- // add an assertion to ensure the callback is executed
- try {
- assert.ok(true, "callback is executed");
- assert.equal(arg1, 'Hello');
- assert.equal(arg2, 'world');
- } finally {
- done();
- };
- },
- 'Hello', 'world');
- deferred.addErrback(function() {
- // throw an exception to start errback chain
- try {
- throw this._error;
- } finally {
- done();
- };
- });
- var done = assert.async();
- var result = jQuery.ajax({
- url: BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html',
- async: false,
- beforeSend: function(xhr) {
- deferred._req = xhr;
- },
- success: function(data, status) {
- deferred.success(data);
- }
- });
- });
-
- QUnit.test('test addErrback', function (assert) {
- assert.expect(1);
- var done = assert.async();
- var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/nonexistent.html');
- d.addCallback(function() {
- // should not be executed
- assert.ok(false, "callback is executed");
- });
- d.addErrback(function() {
- try {
- assert.ok(true, "errback is executed");
- } finally {
- done();
- };
- });
- });
-
- QUnit.test('test callback execution order', function (assert) {
- assert.expect(3);
- var counter = 0;
- var done = assert.async();
- var d = $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html');
- d.addCallback(function() {
- assert.equal(++counter, 1); // should be executed first
- });
- d.addCallback(function() {
- assert.equal(++counter, 2);
- });
- d.addCallback(function() {
- try {
- assert.equal(++counter, 3);
- } finally {
- done();
- }
- });
- });
-
- QUnit.test('test already included resources are ignored (ajax_url1.html)', function (assert) {
- assert.expect(10);
- var scriptsIncluded = jsSources();
- // NOTE:
- assert.equal(jQuery.inArray('http://foo.js', scriptsIncluded), -1);
- assert.equal($('head link').length, 1);
- /* use endswith because in pytest context we have an absolute path */
- assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
- var done = assert.async();
- $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url1.html')
- .addCallback(function() {
- var origLength = scriptsIncluded.length;
- scriptsIncluded = jsSources();
- try {
- // check that foo.js has been inserted in
- assert.equal(scriptsIncluded.length, origLength + 1);
- assert.equal(scriptsIncluded.indexOf('http://foo.js'), 0);
- // check that has been removed
- assert.equal($('#qunit-fixture').children().length, 1);
- assert.equal($('div.ajaxHtmlHead').length, 0);
- assert.equal($('#qunit-fixture h1').html(), 'Hello');
- // qunit.css is not added twice
- assert.equal($('head link').length, 1);
- /* use endswith because in pytest context we have an absolute path */
- assert.ok($('head link').attr('href').endswith('/qunit.css'), 'qunit.css is loaded');
- } finally {
- done();
- }
- }
- );
- });
-
- QUnit.test('test synchronous request loadRemote', function (assert) {
- var res = loadRemote(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajaxresult.json', {},
- 'GET', true);
- assert.deepEqual(res, ['foo', 'bar']);
- });
-
- QUnit.test('test event on CubicWeb', function (assert) {
- assert.expect(1);
- var done = assert.async();
- var events = null;
- $(CubicWeb).bind('server-response', function() {
- // check that server-response event on CubicWeb is triggered
- events = 'CubicWeb';
- });
- $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html')
- .addCallback(function() {
- try {
- assert.equal(events, 'CubicWeb');
- } finally {
- done();
- };
- }
- );
- });
-
- QUnit.test('test event on node', function (assert) {
- assert.expect(3);
- var done = assert.async();
- var nodes = [];
- $('#qunit-fixture').bind('server-response', function() {
- nodes.push('node');
- });
- $(CubicWeb).bind('server-response', function() {
- nodes.push('CubicWeb');
- });
- $('#qunit-fixture').loadxhtml(BASE_URL + 'cwsoftwareroot/web/test/jstests/ajax_url0.html')
- .addCallback(function() {
- try {
- assert.equal(nodes.length, 2);
- // check that server-response event on CubicWeb is triggered
- // only once and event server-response on node is triggered
- assert.equal(nodes[0], 'CubicWeb');
- assert.equal(nodes[1], 'node');
- } finally {
- done();
- };
- }
- );
- });
-});
-
diff -r 3a98422df969 -r 7ceb0971c694 web/test/jstests/test_htmlhelpers.html
--- a/web/test/jstests/test_htmlhelpers.html Tue Dec 15 19:05:41 2015 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- cubicweb.htmlhelpers.js functions tests- -
cw.utils functions tests- -
'))
+ self.assertTrue(source.startswith(b' '))
# def test_json_exec(self):
# rql = 'Any T,N WHERE T is Tag, T name N'
@@ -824,7 +833,7 @@
rset.req = req
source = ctrl.publish()
# maydel jscall
- self.assertIn('ajaxBoxRemoveLinkedEntity', source)
+ self.assertIn(b'ajaxBoxRemoveLinkedEntity', source)
def test_pending_insertion(self):
with self.remote_calling('add_pending_inserts', [['12', 'tags', '13']]) as (_, req):
@@ -887,16 +896,16 @@
# silly tests
def test_external_resource(self):
with self.remote_calling('external_resource', 'RSS_LOGO') as (res, _):
- self.assertEqual(json_dumps(self.config.uiprops['RSS_LOGO']),
+ self.assertEqual(json_dumps(self.config.uiprops['RSS_LOGO']).encode('ascii'),
res)
def test_i18n(self):
with self.remote_calling('i18n', ['bimboom']) as (res, _):
- self.assertEqual(json_dumps(['bimboom']), res)
+ self.assertEqual(json_dumps(['bimboom']).encode('ascii'), res)
def test_format_date(self):
with self.remote_calling('format_date', '2007-01-01 12:00:00') as (res, _):
- self.assertEqual(json_dumps('2007/01/01'), res)
+ self.assertEqual(json_dumps('2007/01/01').encode('ascii'), res)
def test_ajaxfunc_noparameter(self):
@ajaxfunc
@@ -968,7 +977,7 @@
def js_foo(self):
return u'hello'
with self.remote_calling('foo') as (res, _):
- self.assertEqual(res, u'hello')
+ self.assertEqual(res, b'hello')
def test_monkeypatch_jsoncontroller_xhtmlize(self):
with self.assertRaises(RemoteCallFailed):
@@ -979,7 +988,7 @@
def js_foo(self):
return u'hello'
with self.remote_calling('foo') as (res, _):
- self.assertEqual(u' hello ', res)
+ self.assertEqual(b'hello ', res)
def test_monkeypatch_jsoncontroller_jsonize(self):
with self.assertRaises(RemoteCallFailed):
@@ -990,7 +999,7 @@
def js_foo(self):
return 12
with self.remote_calling('foo') as (res, _):
- self.assertEqual(res, '12')
+ self.assertEqual(res, b'12')
def test_monkeypatch_jsoncontroller_stdfunc(self):
@monkeypatch(JSonController)
@@ -998,7 +1007,7 @@
def js_reledit_form(self):
return 12
with self.remote_calling('reledit_form') as (res, _):
- self.assertEqual(res, '12')
+ self.assertEqual(res, b'12')
class UndoControllerTC(CubicWebTC):
@@ -1042,7 +1051,7 @@
"""
with self.admin_access.web_request() as req:
scheme, netloc, path, query, fragment = urlsplit(url)
- query_dict = url_parse_query(query)
+ query_dict = parse_qs(query)
expected_url = urljoin(req.base_url(), expected_path)
self.assertEqual( urlunsplit((scheme, netloc, path, None, None)), expected_url)
@@ -1058,17 +1067,6 @@
result = controller.publish(rset=None)
self.assertURLPath(cm.exception.location, rpath)
- def test_redirect_default(self):
- with self.admin_access.web_request() as req:
- txuuid = self.txuuid_toto_email
- req.form['txuuid'] = txuuid
- req.session.data['breadcrumbs'] = [ urljoin(req.base_url(), path)
- for path in ('tata', 'toto',)]
- controller = self.vreg['controllers'].select('undo', req)
- with self.assertRaises(Redirect) as cm:
- result = controller.publish(rset=None)
- self.assertURLPath(cm.exception.location, 'toto')
-
class LoginControllerTC(CubicWebTC):
diff -r 3a98422df969 -r 7ceb0971c694 web/test/unittest_views_baseviews.py
--- a/web/test/unittest_views_baseviews.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/unittest_views_baseviews.py Wed Dec 16 11:23:48 2015 +0100
@@ -129,8 +129,8 @@
source_lines = [line.strip()
for line in html_source.splitlines(False)
if line.strip()]
- self.assertListEqual(['',
- ''],
+ self.assertListEqual([b'',
+ b''],
source_lines[:2])
def test_set_doctype_no_reset_xmldecl(self):
@@ -151,9 +151,9 @@
source_lines = [line.strip()
for line in html_source.splitlines(False)
if line.strip()]
- self.assertListEqual([html_doctype,
- '',
- ''],
+ self.assertListEqual([html_doctype.encode('ascii'),
+ b'',
+ b''],
source_lines[:3])
if __name__ == '__main__':
diff -r 3a98422df969 -r 7ceb0971c694 web/test/unittest_views_csv.py
--- a/web/test/unittest_views_csv.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/unittest_views_csv.py Wed Dec 16 11:23:48 2015 +0100
@@ -30,19 +30,19 @@
self.assertEqual(req.headers_out.getRawHeaders('content-type'),
['text/comma-separated-values;charset=UTF-8'])
expected_data = "String;COUNT(CWUser)\nguests;1\nmanagers;1"
- self.assertMultiLineEqual(expected_data, data)
+ self.assertMultiLineEqual(expected_data, data.decode('utf-8'))
def test_csvexport_on_empty_rset(self):
"""Should return the CSV header.
"""
with self.admin_access.web_request() as req:
- rset = req.execute('Any GN,COUNT(X) GROUPBY GN ORDERBY GN '
- 'WHERE X in_group G, G name GN, X login "Miles"')
+ rset = req.execute(u'Any GN,COUNT(X) GROUPBY GN ORDERBY GN '
+ 'WHERE X in_group G, G name GN, X login "Miles"')
data = self.view('csvexport', rset, req=req)
self.assertEqual(req.headers_out.getRawHeaders('content-type'),
['text/comma-separated-values;charset=UTF-8'])
expected_data = "String;COUNT(CWUser)"
- self.assertMultiLineEqual(expected_data, data)
+ self.assertMultiLineEqual(expected_data, data.decode('utf-8'))
if __name__ == '__main__':
diff -r 3a98422df969 -r 7ceb0971c694 web/test/unittest_views_editforms.py
--- a/web/test/unittest_views_editforms.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/unittest_views_editforms.py Wed Dec 16 11:23:48 2015 +0100
@@ -255,4 +255,3 @@
if __name__ == '__main__':
unittest_main()
-
diff -r 3a98422df969 -r 7ceb0971c694 web/test/unittest_views_errorform.py
--- a/web/test/unittest_views_errorform.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/unittest_views_errorform.py Wed Dec 16 11:23:48 2015 +0100
@@ -50,8 +50,8 @@
req.data['excinfo'] = sys.exc_info()
req.data['ex'] = e
html = self.view('error', req=req)
- self.failUnless(re.search(r'^$',
+ self.assertTrue(re.search(b'^$',
html.source, re.M))
diff -r 3a98422df969 -r 7ceb0971c694 web/test/unittest_views_json.py
--- a/web/test/unittest_views_json.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/test/unittest_views_json.py Wed Dec 16 11:23:48 2015 +0100
@@ -16,12 +16,14 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see
%s\n' % _(self.title)) for label, group, form in sorted((_(g), g, f) - for g, f in mainforms.iteritems()): + for g, f in mainforms.items()): self.wrap_main_form(group, label, form) for label, group, objects in sorted((_(g), g, o) - for g, o in groupedforms.iteritems()): + for g, o in groupedforms.items()): self.wrap_grouped_form(group, label, objects) @property @@ -171,7 +171,7 @@ entity = self.cwprops_rset.get_entity(values[key], 0) else: entity = self._cw.vreg['etypes'].etype_class('CWProperty')(self._cw) - entity.eid = self._cw.varmaker.next() + entity.eid = next(self._cw.varmaker) entity.cw_attr_cache['pkey'] = key entity.cw_attr_cache['value'] = self._cw.vreg.property_value(key) return entity @@ -224,7 +224,7 @@ (make_togglable_link('fieldset_' + group, label))) self.w(u'' % (group, status))
sorted_objects = sorted((self._cw.__('%s_%s' % (group, o)), o, f)
- for o, f in objects.iteritems())
+ for o, f in objects.items())
for label, oid, form in sorted_objects:
self.wrap_object_form(group, oid, label, form)
self.w(u' ')
diff -r 3a98422df969 -r 7ceb0971c694 web/views/cwsources.py
--- a/web/views/cwsources.py Tue Dec 15 19:05:41 2015 +0100
+++ b/web/views/cwsources.py Wed Dec 16 11:23:48 2015 +0100
@@ -20,20 +20,23 @@
"""
__docformat__ = "restructuredtext en"
-_ = unicode
+from cubicweb import _
import logging
from itertools import repeat
+
+from six.moves import range
+
from logilab.mtconverter import xml_escape
from logilab.common.decorators import cachedproperty
from cubicweb import Unauthorized, tags
from cubicweb.utils import make_uid
from cubicweb.predicates import (is_instance, score_entity, has_related_entities,
- match_user_groups, match_kwargs, match_view)
+ match_user_groups, match_kwargs, match_view, one_line_rset)
from cubicweb.view import EntityView, StartupView
from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, display_name
-from cubicweb.web import formwidgets as wdgs, facet
+from cubicweb.web import Redirect, formwidgets as wdgs, facet, action
from cubicweb.web.views import add_etype_button
from cubicweb.web.views import (uicfg, tabs, actions, ibreadcrumbs, navigation,
tableview, pyviews)
@@ -95,7 +98,7 @@
if hostconfig:
self.w(u'%s' % self._cw._('CWSourceHostConfig_plural')) self._cw.view('table', hostconfig, w=self.w, - displaycols=range(2), + displaycols=list(range(2)), cellvids={1: 'editable-final'}) @@ -186,7 +189,7 @@ warning(_('relation %(rtype)s with %(etype)s as %(role)s is ' 'supported but no target type supported') % {'rtype': rschema, 'role': role, 'etype': etype}) - for rtype, rdefs in self.srelations.iteritems(): + for rtype, rdefs in self.srelations.items(): if rdefs is None: rschema = self.schema[rtype] for subj, obj in rschema.rdefs: @@ -223,6 +226,36 @@ layout_args = {'display_filter': 'top'} +class CWSourceSyncAction(action.Action): + __regid__ = 'cw.source-sync' + __select__ = (action.Action.__select__ & match_user_groups('managers') + & one_line_rset() & is_instance('CWSource') + & score_entity(lambda x: x.name != 'system')) + + title = _('synchronize') + category = 'mainactions' + order = 20 + + def url(self): + entity = self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0) + return entity.absolute_url(vid=self.__regid__) + + +class CWSourceSyncView(EntityView): + __regid__ = 'cw.source-sync' + __select__ = (match_user_groups('managers') + & one_line_rset() & is_instance('CWSource') + & score_entity(lambda x: x.name != 'system')) + + title = _('synchronize') + + def entity_call(self, entity): + self._cw.call_service('source-sync', source_eid=entity.eid) + msg = self._cw._('Source has been synchronized') + url = entity.absolute_url(tab='cwsource-imports', __message=msg) + raise Redirect(url) + + # sources management view ###################################################### diff -r 3a98422df969 -r 7ceb0971c694 web/views/cwuser.py --- a/web/views/cwuser.py Tue Dec 15 19:05:41 2015 +0100 +++ b/web/views/cwuser.py Wed Dec 16 11:23:48 2015 +0100 @@ -18,10 +18,13 @@ """Specific views for users and groups""" __docformat__ = "restructuredtext en" -_ = unicode +from cubicweb import _ from hashlib import sha1 # pylint: disable=E0611 +from six import text_type +from six.moves import range + from logilab.mtconverter import xml_escape from cubicweb import tags @@ -64,7 +67,7 @@
%s' % _('Instance')) - w(u'
%s' % _('versions configuration')) - w(u'
%s' % _('Repository')) w(u'%s' % _('resources usage')) - w(u'
%s' % _('opened sessions')) sessions = repo._sessions.values() @@ -116,7 +112,7 @@ w(u'
|