merge two default heads
authorJulien Cristau <julien.cristau@logilab.fr>
Wed, 09 Oct 2013 11:13:56 +0200
changeset 9265 614762cdc357
parent 9261 1dfe62a9da50 (diff)
parent 9246 593b29325f68 (current diff)
child 9283 5f2c5eb1a820
merge two default heads
--- a/.hgtags	Mon Sep 30 18:07:51 2013 +0200
+++ b/.hgtags	Wed Oct 09 11:13:56 2013 +0200
@@ -305,3 +305,9 @@
 5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-centos-version-3.17.6-1
 5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-version-3.17.6
 5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-debian-version-3.17.6-1
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-centos-version-3.17.7-1
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-version-3.17.7
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-debian-version-3.17.7-1
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-centos-version-3.17.8-1
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-version-3.17.8
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-debian-version-3.17.8-1
--- a/__pkginfo__.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/__pkginfo__.py	Wed Oct 09 11:13:56 2013 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 17, 7)
+numversion = (3, 17, 8)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/cubicweb.spec	Mon Sep 30 18:07:51 2013 +0200
+++ b/cubicweb.spec	Wed Oct 09 11:13:56 2013 +0200
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.17.7
+Version:        3.17.8
 Release:        logilab.1%{?dist}
 Summary:        CubicWeb is a semantic web application framework
 Source0:        http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
--- a/cwconfig.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/cwconfig.py	Wed Oct 09 11:13:56 2013 +0200
@@ -53,8 +53,7 @@
 
 If you are not administrator of you machine or if you need to play with some
 specific version of |cubicweb| you can use `virtualenv`_ a tool to create
-isolated Python environments.  Since version 3.9 |cubicweb| is **`virtualenv`
-friendly** and won't write any file outside the virtualenv directory.
+isolated Python environments.
 
 - instances are stored in :file:`<VIRTUAL_ENV>/etc/cubicweb.d`
 - temporary files (such as pid file) in :file:`<VIRTUAL_ENV>/var/run/cubicweb`
@@ -206,7 +205,7 @@
     """return a list of installed configurations in a directory
     according to \*-ctl files
     """
-    return [name for name in ('repository', 'twisted', 'all-in-one')
+    return [name for name in ('repository', 'all-in-one')
             if exists(join(directory, '%s.conf' % name))]
 
 def guess_configuration(directory):
@@ -328,7 +327,7 @@
     # the format below can be useful to debug multi thread issues:
     # log_format = '%(asctime)s - [%(threadName)s] (%(name)s) %(levelname)s: %(message)s'
     # nor remove appobjects based on unused interface [???]
-    cleanup_interface_sobjects = True
+    cleanup_unused_appobjects = True
 
     if (CWDEV and _forced_mode != 'system'):
         mode = 'user'
@@ -499,21 +498,11 @@
             try:
                 gendeps = getattr(pkginfo, key.replace('_cubes', ''))
             except AttributeError:
-                # bw compat
-                if hasattr(pkginfo, oldkey):
-                    warn('[3.8] cube %s: %s is deprecated, use %s dict'
-                         % (cube, oldkey, key), DeprecationWarning)
-                    deps = getattr(pkginfo, oldkey)
-                else:
-                    deps = {}
+                deps = {}
             else:
                 deps = dict( (x[len('cubicweb-'):], v)
                              for x, v in gendeps.iteritems()
                              if x.startswith('cubicweb-'))
-        if not isinstance(deps, dict):
-            deps = dict((key, None) for key in deps)
-            warn('[3.8] cube %s should define %s as a dict' % (cube, key),
-                 DeprecationWarning)
         for depcube in deps:
             try:
                 newname = CW_MIGRATION_MAP[depcube]
@@ -940,10 +929,9 @@
                                      ' "cubicweb-ctl list")' % appid)
         return home
 
-    MODES = ('common', 'repository', 'Any', 'web')
+    MODES = ('common', 'repository', 'Any')
     MCOMPAT = {'all-in-one': MODES,
-               'repository': ('common', 'repository', 'Any'),
-               'twisted'   : ('common', 'web'),}
+               'repository': ('common', 'repository', 'Any')}
     @classmethod
     def accept_mode(cls, mode):
         #assert mode in cls.MODES, mode
--- a/cwctl.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/cwctl.py	Wed Oct 09 11:13:56 2013 +0200
@@ -266,12 +266,7 @@
                         if tinfo:
                             descr = getattr(tinfo, 'description', '')
                             if not descr:
-                                descr = getattr(tinfo, 'short_desc', '')
-                                if descr:
-                                    warn('[3.8] short_desc is deprecated, update %s'
-                                         ' pkginfo' % cube, DeprecationWarning)
-                                else:
-                                    descr = tinfo.__doc__
+                                descr = tinfo.__doc__
                             if descr:
                                 print '    '+ '    \n'.join(descr.splitlines())
                         modes = detect_available_modes(cwcfg.cube_dir(cube))
@@ -357,7 +352,7 @@
           }),
         ('config',
          {'short': 'c', 'type' : 'choice', 'metavar': '<install type>',
-          'choices': ('all-in-one', 'repository', 'twisted'),
+          'choices': ('all-in-one', 'repository'),
           'default': 'all-in-one',
           'help': 'installation type, telling which part of an instance '
           'should be installed. You can list available configurations using the'
--- a/cwvreg.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/cwvreg.py	Wed Oct 09 11:13:56 2013 +0200
@@ -211,8 +211,7 @@
 
 from cubicweb import (CW_SOFTWARE_ROOT, ETYPE_NAME_MAP, CW_EVENT_MANAGER,
                       onevent, Binary, UnknownProperty, UnknownEid)
-from cubicweb.predicates import (implements, appobject_selectable,
-                                 _reset_is_instance_cache)
+from cubicweb.predicates import appobject_selectable, _reset_is_instance_cache
 
 
 @onevent('before-registry-reload')
@@ -230,15 +229,6 @@
     sys.modules.pop('cubicweb.web.uicfg', None)
     sys.modules.pop('cubicweb.web.uihelper', None)
 
-def use_interfaces(obj):
-    """return interfaces required by the given object by searching for
-    `implements` predicate
-    """
-    impl = obj.__select__.search_selector(implements)
-    if impl:
-        return sorted(impl.expected_ifaces)
-    return ()
-
 def require_appobject(obj):
     """return appobjects required by the given object by searching for
     `appobject_selectable` predicate
@@ -568,7 +558,6 @@
     def reset(self):
         CW_EVENT_MANAGER.emit('before-registry-reset', self)
         super(CWRegistryStore, self).reset()
-        self._needs_iface = {}
         self._needs_appobject = {}
         # two special registries, propertydefs which care all the property
         # definitions, and propertyvals which contains values for those
@@ -641,20 +630,6 @@
                 for obj in objects:
                     obj.schema = schema
 
-    @deprecated('[3.9] use .register instead')
-    def register_if_interface_found(self, obj, ifaces, **kwargs):
-        """register `obj` but remove it if no entity class implements one of
-        the given `ifaces` interfaces at the end of the registration process.
-
-        Extra keyword arguments are given to the
-        :meth:`~cubicweb.cwvreg.CWRegistryStore.register` function.
-        """
-        self.register(obj, **kwargs)
-        if not isinstance(ifaces,  (tuple, list)):
-            self._needs_iface[obj] = (ifaces,)
-        else:
-            self._needs_iface[obj] = ifaces
-
     def register(self, obj, *args, **kwargs):
         """register `obj` application object into `registryname` or
         `obj.__registry__` if not specified, with identifier `oid` or
@@ -665,15 +640,6 @@
         """
         obj = related_appobject(obj)
         super(CWRegistryStore, self).register(obj, *args, **kwargs)
-        # XXX bw compat
-        ifaces = use_interfaces(obj)
-        if ifaces:
-            if not obj.__name__.endswith('Adapter') and \
-                   any(iface for iface in ifaces if not isinstance(iface, basestring)):
-                warn('[3.9] %s: interfaces in implements selector are '
-                     'deprecated in favor of adapters / adaptable '
-                     'selector' % obj.__name__, DeprecationWarning)
-            self._needs_iface[obj] = ifaces
         depends_on = require_appobject(obj)
         if depends_on is not None:
             self._needs_appobject[obj] = depends_on
@@ -687,41 +653,14 @@
     def initialization_completed(self):
         """cw specific code once vreg initialization is completed:
 
-        * remove objects requiring a missing interface, unless
-          config.cleanup_interface_sobjects is false
+        * remove objects requiring a missing appobject, unless
+          config.cleanup_unused_appobjects is false
         * init rtags
         """
         # we may want to keep interface dependent objects (e.g.for i18n
         # catalog generation)
-        if self.config.cleanup_interface_sobjects:
-            # XXX deprecated with cw 3.9: remove appobjects that don't support
-            # any available interface
-            implemented_interfaces = set()
-            if 'Any' in self.get('etypes', ()):
-                for etype in self.schema.entities():
-                    if etype.final:
-                        continue
-                    cls = self['etypes'].etype_class(etype)
-                    if cls.__implements__:
-                        warn('[3.9] %s: using __implements__/interfaces are '
-                             'deprecated in favor of adapters' % cls.__name__,
-                             DeprecationWarning)
-                    for iface in cls.__implements__:
-                        implemented_interfaces.update(iface.__mro__)
-                    implemented_interfaces.update(cls.__mro__)
-            for obj, ifaces in self._needs_iface.items():
-                ifaces = frozenset(isinstance(iface, basestring)
-                                   and iface in self.schema
-                                   and self['etypes'].etype_class(iface)
-                                   or iface
-                                   for iface in ifaces)
-                if not ('Any' in ifaces or ifaces & implemented_interfaces):
-                    reg = self[obj_registries(obj)[0]]
-                    self.debug('unregister %s (no implemented '
-                               'interface among %s)', reg.objid(obj), ifaces)
-                    self.unregister(obj)
-            # since 3.9: remove appobjects which depending on other, unexistant
-            # appobjects
+        if self.config.cleanup_unused_appobjects:
+            # remove appobjects which depend on other, unexistant appobjects
             for obj, (regname, regids) in self._needs_appobject.items():
                 try:
                     registry = self[regname]
@@ -740,8 +679,8 @@
         if 'uicfg' in self: # 'uicfg' is not loaded in a pure repository mode
             for rtags in self['uicfg'].itervalues():
                 for rtag in rtags:
-                    # don't check rtags if we don't want to cleanup_interface_sobjects
-                    rtag.init(self.schema, check=self.config.cleanup_interface_sobjects)
+                    # don't check rtags if we don't want to cleanup_unused_appobjects
+                    rtag.init(self.schema, check=self.config.cleanup_unused_appobjects)
 
     # rql parsing utilities ####################################################
 
--- a/dbapi.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/dbapi.py	Wed Oct 09 11:13:56 2013 +0200
@@ -422,25 +422,6 @@
         req.set_session(self.session, user)
         return req
 
-    @deprecated('[3.8] use direct access to req.session.data dictionary')
-    def session_data(self):
-        """return a dictionary containing session data"""
-        return self.session.data
-
-    @deprecated('[3.8] use direct access to req.session.data dictionary')
-    def get_session_data(self, key, default=None, pop=False):
-        if pop:
-            return self.session.data.pop(key, default)
-        return self.session.data.get(key, default)
-
-    @deprecated('[3.8] use direct access to req.session.data dictionary')
-    def set_session_data(self, key, value):
-        self.session.data[key] = value
-
-    @deprecated('[3.8] use direct access to req.session.data dictionary')
-    def del_session_data(self, key):
-        self.session.data.pop(key, None)
-
     # these are overridden by set_log_methods below
     # only defining here to prevent pylint from complaining
     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
@@ -486,7 +467,7 @@
     def _txid(self):
         return self.connection._txid(self)
 
-    def execute(self, rql, args=None, eid_key=None, build_descr=True):
+    def execute(self, rql, args=None, build_descr=True):
         """execute a rql query, return resulting rows and their description in
         a :class:`~cubicweb.rset.ResultSet` object
 
@@ -517,10 +498,6 @@
 
             execute('Any X WHERE X eid %(x)s', {'x': 123})
         """
-        if eid_key is not None:
-            warn('[3.8] eid_key is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
-        # XXX use named argument for build_descr in case repo is < 3.8
         rset = self._repo.execute(self._sessid, rql, args,
                                   build_descr=build_descr, **self._txid())
         rset.req = self.req
--- a/debian/changelog	Mon Sep 30 18:07:51 2013 +0200
+++ b/debian/changelog	Wed Oct 09 11:13:56 2013 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.17.8-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- David Douard <david.douard@logilab.fr>  Thu, 03 Oct 2013 15:12:32 +0200
+
 cubicweb (3.17.7-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Mon Sep 30 18:07:51 2013 +0200
+++ b/debian/control	Wed Oct 09 11:13:56 2013 +0200
@@ -131,6 +131,8 @@
  python-fyzz,
  python-imaging,
  python-rdflib
+Breaks:
+ cubicweb-inlinedit (<< 1.1.1),
 Description: web interface library for the CubicWeb framework
  CubicWeb is a semantic web application framework.
  .
@@ -158,6 +160,9 @@
  python-crypto
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
+Breaks:
+ cubicweb-comment (<< 1.9.1),
+ cubicweb-person (<< 1.8.0),
 Description: common library for the CubicWeb framework
  CubicWeb is a semantic web application framework.
  .
--- a/devtools/__init__.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/devtools/__init__.py	Wed Oct 09 11:13:56 2013 +0200
@@ -37,7 +37,7 @@
 from cubicweb import ExecutionError, BadConnectionId
 from cubicweb import schema, cwconfig
 from cubicweb.server.serverconfig import ServerConfiguration
-from cubicweb.etwist.twconfig import TwistedConfiguration
+from cubicweb.etwist.twconfig import WebConfigurationBase
 
 cwconfig.CubicWebConfiguration.cls_adjust_sys_path()
 
@@ -213,12 +213,12 @@
         return BASE_URL
 
 
-class BaseApptestConfiguration(TestServerConfiguration, TwistedConfiguration):
+class BaseApptestConfiguration(TestServerConfiguration, WebConfigurationBase):
     name = 'all-in-one' # so it search for all-in-one.conf, not repository.conf
     options = cwconfig.merge_options(TestServerConfiguration.options
-                                     + TwistedConfiguration.options)
-    cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | TwistedConfiguration.cubicweb_appobject_path
-    cube_appobject_path = TestServerConfiguration.cube_appobject_path | TwistedConfiguration.cube_appobject_path
+                                     + WebConfigurationBase.options)
+    cubicweb_appobject_path = TestServerConfiguration.cubicweb_appobject_path | WebConfigurationBase.cubicweb_appobject_path
+    cube_appobject_path = TestServerConfiguration.cube_appobject_path | WebConfigurationBase.cube_appobject_path
 
     def available_languages(self, *args):
         return self.cw_languages()
--- a/devtools/devctl.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/devtools/devctl.py	Wed Oct 09 11:13:56 2013 +0200
@@ -46,7 +46,7 @@
     a cube or for cubicweb (without a home)
     """
     creating = True
-    cleanup_interface_sobjects = False
+    cleanup_unused_appobjects = False
 
     cubicweb_appobject_path = (ServerConfiguration.cubicweb_appobject_path
                                | WebConfiguration.cubicweb_appobject_path)
--- a/devtools/testlib.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/devtools/testlib.py	Wed Oct 09 11:13:56 2013 +0200
@@ -419,13 +419,10 @@
         return self.cnx.cursor(req or self.request())
 
     @nocoverage
-    def execute(self, rql, args=None, eidkey=None, req=None):
+    def execute(self, rql, args=None, req=None):
         """executes <rql>, builds a resultset, and returns a couple (rset, req)
         where req is a FakeRequest
         """
-        if eidkey is not None:
-            warn('[3.8] eidkey is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
         req = req or self.request(rql=rql)
         return req.execute(unicode(rql), args)
 
@@ -447,10 +444,7 @@
 
     # server side db api #######################################################
 
-    def sexecute(self, rql, args=None, eid_key=None):
-        if eid_key is not None:
-            warn('[3.8] eid_key is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
+    def sexecute(self, rql, args=None):
         self.session.set_cnxset()
         return self.session.execute(rql, args)
 
@@ -986,15 +980,6 @@
             self.assertEqual(len(MAILBOX), nb_msgs)
         return messages
 
-    # deprecated ###############################################################
-
-    @deprecated('[3.8] use self.execute(...).get_entity(0, 0)')
-    def entity(self, rql, args=None, eidkey=None, req=None):
-        if eidkey is not None:
-            warn('[3.8] eidkey is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
-        return self.execute(rql, args, req=req).get_entity(0, 0)
-
 
 # auto-populating test classes and utilities ###################################
 
--- a/doc/3.18.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/3.18.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -11,6 +11,9 @@
 API changes
 -----------
 
+* not really an API change, but the entity permission checks are now
+  systematically deferred to an operation, instead of a) trying in a
+  hook and b) if it failed, retrying later in an operation
 
 
 Deprecation
@@ -24,3 +27,6 @@
 * ``ldapuser`` have been dropped; use ``ldapfeed`` now
   (see `#2936496 <http://www.cubicweb.org/2936496>`_)
 
+* all 3.8 backward compat is gone
+
+* all 3.9 backward compat (including the javascript side) is gone
--- a/doc/book/en/admin/setup.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/admin/setup.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -121,10 +121,10 @@
 `Virtualenv` install
 --------------------
 
-Since version 3.9, |cubicweb| can be safely installed, used and contained inside
-a `virtualenv`_. You can use either :ref:`pip <PipInstallation>` or
-:ref:`easy_install <EasyInstallInstallation>` to install |cubicweb| inside an
-activated virtual environment.
+|cubicweb| can be safely installed, used and contained inside a
+`virtualenv`_. You can use either :ref:`pip <PipInstallation>` or
+:ref:`easy_install <EasyInstallInstallation>` to install |cubicweb|
+inside an activated virtual environment.
 
 .. _PipInstallation:
 
--- a/doc/book/en/devrepo/datamodel/definition.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/devrepo/datamodel/definition.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -226,13 +226,13 @@
 * `SizeConstraint`: allows to specify a minimum and/or maximum size on
   string (generic case of `maxsize`)
 
-* `BoundConstraint`: allows to specify a minimum and/or maximum value
+* `BoundaryConstraint`: allows to specify a minimum and/or maximum value
   on numeric types and date
 
 .. sourcecode:: python
 
-   from yams.constraints import BoundConstraint, TODAY
-   BoundConstraint('<=', TODAY())
+   from yams.constraints import BoundaryConstraint, TODAY
+   BoundaryConstraint('<=', TODAY())
 
 * `IntervalBoundConstraint`: allows to specify an interval with
   included values
--- a/doc/book/en/devrepo/entityclasses/adapters.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/devrepo/entityclasses/adapters.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -10,13 +10,7 @@
 .. _`interfaces`: http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
 .. _`adapter`: http://en.wikipedia.org/wiki/Adapter_pattern
 
-In |cubicweb| adapters provide logical functionalities to entity types. They
-are introduced in version `3.9`. Before that one had to implement Interfaces in
-entity classes to achieve a similar goal. However, the problem with this
-approach is that is clutters the entity class's namespace, exposing name
-collision risks with schema attributes/relations or even methods names
-(different interfaces may define the same method with not necessarily the same
-behaviour expected).
+In |cubicweb| adapters provide logical functionalities to entity types.
 
 Definition of an adapter is quite trivial. An excerpt from cubicweb
 itself (found in :mod:`cubicweb.entities.adapters`):
--- a/doc/book/en/devrepo/testing.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/devrepo/testing.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -18,12 +18,7 @@
 convenience methods to help test all of this.
 
 In the realm of views, automatic tests check that views are valid
-XHTML. See :ref:`automatic_views_tests` for details. Since 3.9, bases
-for web functional testing using `windmill
-<http://www.getwindmill.com/>`_ are set. See test cases in
-cubicweb/web/test/windmill and python wrapper in
-cubicweb/web/test_windmill/ if you want to use this in your own cube.
-
+XHTML. See :ref:`automatic_views_tests` for details.
 
 Most unit tests need a live database to work against. This is achieved
 by CubicWeb using automatically sqlite (bundled with Python, see
--- a/doc/book/en/tutorials/advanced/part04_ui-base.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part04_ui-base.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -194,8 +194,6 @@
 
 .. Note::
 
-    * Adapters have been introduced in CubicWeb 3.9 / cubicweb-folder 1.8.
-
     * As seen earlier, we want to **replace** the folder's `ITree` adapter by our
       implementation, hence the custom `registration_callback` method.
 
@@ -241,12 +239,6 @@
 ascendant/descendant ordering and a strict comparison with current file's name
 (the "X" variable representing the current file).
 
-.. Note::
-
-    * Former `implements` selector should be replaced by one of `is_instance` /
-      `adaptable` selector with CubicWeb >= 3.9. In our case, `is_instance` to
-      tell our adapter is able to adapt `File` entities.
-
 Notice that this query supposes we wont have two files of the same name in the
 same folder, else things may go wrong. Fixing this is out of the scope of this
 blog. And as I would like to have at some point a smarter, context sensitive
@@ -358,7 +350,7 @@
 You'll have to answer some questions, as we've seen in `an earlier post`_.
 
 Now that everything is tested, I can transfer the new code to the production
-server, `apt-get upgrade` cubicweb 3.9 and its dependencies, and eventually
+server, `apt-get upgrade` cubicweb and its dependencies, and eventually
 upgrade the production instance.
 
 
--- a/doc/book/en/tutorials/advanced/part05_ui-advanced.rst	Mon Sep 30 18:07:51 2013 +0200
+++ b/doc/book/en/tutorials/advanced/part05_ui-advanced.rst	Wed Oct 09 11:13:56 2013 +0200
@@ -1,8 +1,6 @@
 Building my photos web site with |cubicweb| part V: let's make it even more user friendly
 =========================================================================================
 
-We'll now see how to benefit from features introduced in 3.9 and 3.10 releases of CubicWeb
-
 .. _uiprops:
 
 Step 1: tired of the default look?
@@ -29,9 +27,9 @@
 
    LOGO = data('logo.jpg')
 
-The uiprops machinery has been introduced in `CubicWeb 3.9`_. It is used to define
-some static file resources, such as the logo, default Javascript / CSS files, as
-well as CSS properties (we'll see that later).
+The uiprops machinery is used to define some static file resources,
+such as the logo, default Javascript / CSS files, as well as CSS
+properties (we'll see that later).
 
 .. Note::
    This file is imported specifically by |cubicweb|, with a predefined name space,
@@ -373,5 +371,4 @@
 
 
 .. _`CubicWeb 3.10`: http://www.cubicweb.org/blogentry/1330518
-.. _`CubicWeb 3.9`: http://www.cubicweb.org/blogentry/1179899
 .. _`here`: http://webdesign.about.com/od/css3/f/blfaqbgsize.htm
--- a/entities/adapters.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/entities/adapters.py	Wed Oct 09 11:13:56 2013 +0200
@@ -28,9 +28,7 @@
 from logilab.common.decorators import cached
 
 from cubicweb import ValidationError, view
-from cubicweb.predicates import (implements, is_instance, relation_possible,
-                                match_exception)
-from cubicweb.interfaces import IDownloadable, ITree
+from cubicweb.predicates import is_instance, relation_possible, match_exception
 
 
 class IEmailableAdapter(view.EntityAdapter):
@@ -67,11 +65,9 @@
 
 
 class INotifiableAdapter(view.EntityAdapter):
-    __needs_bw_compat__ = True
     __regid__ = 'INotifiable'
     __select__ = is_instance('Any')
 
-    @view.implements_adapter_compat('INotifiableAdapter')
     def notification_references(self, view):
         """used to control References field of email send on notification
         for this entity. `view` is the notification view.
@@ -167,27 +163,25 @@
 
 class IDownloadableAdapter(view.EntityAdapter):
     """interface for downloadable entities"""
-    __needs_bw_compat__ = True
     __regid__ = 'IDownloadable'
-    __select__ = implements(IDownloadable, warn=False) # XXX for bw compat, else should be abstract
+    __abstract__ = True
 
-    @view.implements_adapter_compat('IDownloadable')
     def download_url(self, **kwargs): # XXX not really part of this interface
         """return an url to download entity's content"""
         raise NotImplementedError
-    @view.implements_adapter_compat('IDownloadable')
+
     def download_content_type(self):
         """return MIME type of the downloadable content"""
         raise NotImplementedError
-    @view.implements_adapter_compat('IDownloadable')
+
     def download_encoding(self):
         """return encoding of the downloadable content"""
         raise NotImplementedError
-    @view.implements_adapter_compat('IDownloadable')
+
     def download_file_name(self):
         """return file name of the downloadable content"""
         raise NotImplementedError
-    @view.implements_adapter_compat('IDownloadable')
+
     def download_data(self):
         """return actual data of the downloadable content"""
         raise NotImplementedError
@@ -219,27 +213,16 @@
     .. automethod: children_rql
     .. automethod: path
     """
-    __needs_bw_compat__ = True
     __regid__ = 'ITree'
-    __select__ = implements(ITree, warn=False) # XXX for bw compat, else should be abstract
+    __abstract__ = True
 
     child_role = 'subject'
     parent_role = 'object'
 
-    @property
-    def tree_relation(self):
-        warn('[3.9] tree_attribute is deprecated, define tree_relation on a custom '
-             'ITree for %s instead' % (self.entity.__class__),
-             DeprecationWarning)
-        return self.entity.tree_attribute
-
-    # XXX should be removed from the public interface
-    @view.implements_adapter_compat('ITree')
     def children_rql(self):
         """Returns RQL to get the children of the entity."""
         return self.entity.cw_related_rql(self.tree_relation, self.parent_role)
 
-    @view.implements_adapter_compat('ITree')
     def different_type_children(self, entities=True):
         """Return children entities of different type as this entity.
 
@@ -253,7 +236,6 @@
             return [e for e in res if e.e_schema != eschema]
         return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col)
 
-    @view.implements_adapter_compat('ITree')
     def same_type_children(self, entities=True):
         """Return children entities of the same type as this entity.
 
@@ -267,23 +249,19 @@
             return [e for e in res if e.e_schema == eschema]
         return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col)
 
-    @view.implements_adapter_compat('ITree')
     def is_leaf(self):
         """Returns True if the entity does not have any children."""
         return len(self.children()) == 0
 
-    @view.implements_adapter_compat('ITree')
     def is_root(self):
         """Returns true if the entity is root of the tree (e.g. has no parent).
         """
         return self.parent() is None
 
-    @view.implements_adapter_compat('ITree')
     def root(self):
         """Return the root entity of the tree."""
         return self._cw.entity_from_eid(self.path()[0])
 
-    @view.implements_adapter_compat('ITree')
     def parent(self):
         """Returns the parent entity if any, else None (e.g. if we are on the
         root).
@@ -294,7 +272,6 @@
         except (KeyError, IndexError):
             return None
 
-    @view.implements_adapter_compat('ITree')
     def children(self, entities=True, sametype=False):
         """Return children entities.
 
@@ -307,7 +284,6 @@
             return self.entity.related(self.tree_relation, self.parent_role,
                                        entities=entities)
 
-    @view.implements_adapter_compat('ITree')
     def iterparents(self, strict=True):
         """Return an iterator on the parents of the entity."""
         def _uptoroot(self):
@@ -322,7 +298,6 @@
             return chain([self.entity], _uptoroot(self))
         return _uptoroot(self)
 
-    @view.implements_adapter_compat('ITree')
     def iterchildren(self, _done=None):
         """Return an iterator over the item's children."""
         if _done is None:
@@ -334,7 +309,6 @@
             yield child
             _done.add(child.eid)
 
-    @view.implements_adapter_compat('ITree')
     def prefixiter(self, _done=None):
         """Return an iterator over the item's descendants in a prefixed order."""
         if _done is None:
@@ -347,7 +321,6 @@
             for entity in child.cw_adapt_to('ITree').prefixiter(_done):
                 yield entity
 
-    @view.implements_adapter_compat('ITree')
     @cached
     def path(self):
         """Returns the list of eids from the root object to this object."""
--- a/entities/test/unittest_base.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/entities/test/unittest_base.py	Wed Oct 09 11:13:56 2013 +0200
@@ -25,7 +25,6 @@
 
 from cubicweb.devtools.testlib import CubicWebTC
 
-from cubicweb.interfaces import IMileStone, ICalendarable
 from cubicweb.entities import AnyEntity
 
 
@@ -134,27 +133,6 @@
         self.request().create_entity('CWGroup', name=u'logilab', reverse_in_group=e)
 
 
-class InterfaceTC(CubicWebTC):
-
-    def test_nonregr_subclasses_and_mixins_interfaces(self):
-        from cubicweb.entities.wfobjs import WorkflowableMixIn
-        WorkflowableMixIn.__implements__ = (ICalendarable,)
-        CWUser = self.vreg['etypes'].etype_class('CWUser')
-        class MyUser(CWUser):
-            __implements__ = (IMileStone,)
-        self.vreg._loadedmods[__name__] = {}
-        self.vreg.register(MyUser)
-        self.vreg['etypes'].initialization_completed()
-        MyUser_ = self.vreg['etypes'].etype_class('CWUser')
-        # a copy is done systematically
-        self.assertTrue(issubclass(MyUser_, MyUser))
-        self.assertTrue(implements(MyUser_, IMileStone))
-        self.assertTrue(implements(MyUser_, ICalendarable))
-        # original class should not have beed modified, only the copy
-        self.assertTrue(implements(MyUser, IMileStone))
-        self.assertFalse(implements(MyUser, ICalendarable))
-
-
 class SpecializedEntityClassesTC(CubicWebTC):
 
     def select_eclass(self, etype):
--- a/entities/wfobjs.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/entities/wfobjs.py	Wed Oct 09 11:13:56 2013 +0200
@@ -32,7 +32,6 @@
 from cubicweb.entities import AnyEntity, fetch_config
 from cubicweb.view import EntityAdapter
 from cubicweb.predicates import relation_possible
-from cubicweb.mixins import MI_REL_TRIGGERS
 
 class WorkflowException(Exception): pass
 
@@ -379,65 +378,8 @@
         return self.by_transition and self.by_transition[0] or None
 
 
-class WorkflowableMixIn(object):
-    """base mixin providing workflow helper methods for workflowable entities.
-    This mixin will be automatically set on class supporting the 'in_state'
-    relation (which implies supporting 'wf_info_for' as well)
-    """
 
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').main_workflow")
-    def main_workflow(self):
-        return self.cw_adapt_to('IWorkflowable').main_workflow
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_workflow")
-    def current_workflow(self):
-        return self.cw_adapt_to('IWorkflowable').current_workflow
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').current_state")
-    def current_state(self):
-        return self.cw_adapt_to('IWorkflowable').current_state
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').state")
-    def state(self):
-        return self.cw_adapt_to('IWorkflowable').state
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').printable_state")
-    def printable_state(self):
-        return self.cw_adapt_to('IWorkflowable').printable_state
-    @property
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').workflow_history")
-    def workflow_history(self):
-        return self.cw_adapt_to('IWorkflowable').workflow_history
-
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').cwetype_workflow()")
-    def cwetype_workflow(self):
-        return self.cw_adapt_to('IWorkflowable').main_workflow()
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').latest_trinfo()")
-    def latest_trinfo(self):
-        return self.cw_adapt_to('IWorkflowable').latest_trinfo()
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').possible_transitions()")
-    def possible_transitions(self, type='normal'):
-        return self.cw_adapt_to('IWorkflowable').possible_transitions(type)
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').fire_transition()")
-    def fire_transition(self, tr, comment=None, commentformat=None):
-        return self.cw_adapt_to('IWorkflowable').fire_transition(tr, comment, commentformat)
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').change_state()")
-    def change_state(self, statename, comment=None, commentformat=None, tr=None):
-        return self.cw_adapt_to('IWorkflowable').change_state(statename, comment, commentformat, tr)
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()")
-    def subworkflow_input_trinfo(self):
-        return self.cw_adapt_to('IWorkflowable').subworkflow_input_trinfo()
-    @deprecated("[3.9] use entity.cw_adapt_to('IWorkflowable').subworkflow_input_transition()")
-    def subworkflow_input_transition(self):
-        return self.cw_adapt_to('IWorkflowable').subworkflow_input_transition()
-
-
-MI_REL_TRIGGERS[('in_state', 'subject')] = WorkflowableMixIn
-
-
-
-class IWorkflowableAdapter(WorkflowableMixIn, EntityAdapter):
+class IWorkflowableAdapter(EntityAdapter):
     """base adapter providing workflow helper methods for workflowable entities.
     """
     __regid__ = 'IWorkflowable'
--- a/entity.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/entity.py	Wed Oct 09 11:13:56 2013 +0200
@@ -42,7 +42,6 @@
 from cubicweb.rqlrewrite import RQLRewriter
 
 from cubicweb.uilib import soup2xhtml
-from cubicweb.mixins import MI_REL_TRIGGERS
 from cubicweb.mttransforms import ENGINE
 
 _marker = object()
@@ -194,31 +193,11 @@
             setattr(cls, rschema.type, Attribute(rschema.type))
         mixins = []
         for rschema, _, role in eschema.relation_definitions():
-            if (rschema, role) in MI_REL_TRIGGERS:
-                mixin = MI_REL_TRIGGERS[(rschema, role)]
-                if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
-                    mixins.append(mixin)
-                for iface in getattr(mixin, '__implements__', ()):
-                    if not interface.implements(cls, iface):
-                        interface.extend(cls, iface)
             if role == 'subject':
                 attr = rschema.type
             else:
                 attr = 'reverse_%s' % rschema.type
             setattr(cls, attr, Relation(rschema, role))
-        if mixins:
-            # see etype class instantation in cwvreg.ETypeRegistry.etype_class method:
-            # due to class dumping, cls is the generated top level class with actual
-            # user class as (only) parent. Since we want to be able to override mixins
-            # method from this user class, we have to take care to insert mixins after that
-            # class
-            #
-            # note that we don't plug mixins as user class parent since it causes pb
-            # with some cases of entity classes inheritance.
-            mixins.insert(0, cls.__bases__[0])
-            mixins += cls.__bases__[1:]
-            cls.__bases__ = tuple(mixins)
-            cls.info('plugged %s mixins on %s', mixins, cls)
 
     fetch_attrs = ('modification_date',)
 
@@ -558,7 +537,14 @@
         raise NotImplementedError('comparison not implemented for %s' % self.__class__)
 
     def __eq__(self, other):
-        raise NotImplementedError('comparison not implemented for %s' % self.__class__)
+        if isinstance(self.eid, (int, long)):
+            return self.eid == other.eid
+        return self is other
+
+    def __hash__(self):
+        if isinstance(self.eid, (int, long)):
+            return self.eid
+        return super(Entity, self).__hash__()
 
     def _cw_update_attr_cache(self, attrcache):
         # if context is a repository session, don't consider dont-cache-attrs as
@@ -1338,34 +1324,6 @@
     def clear_all_caches(self):
         return self.cw_clear_all_caches()
 
-    @deprecated('[3.9] use entity.cw_attr_value(attr)')
-    def get_value(self, name):
-        return self.cw_attr_value(name)
-
-    @deprecated('[3.9] use entity.cw_delete()')
-    def delete(self, **kwargs):
-        return self.cw_delete(**kwargs)
-
-    @deprecated('[3.9] use entity.cw_attr_metadata(attr, metadata)')
-    def attr_metadata(self, attr, metadata):
-        return self.cw_attr_metadata(attr, metadata)
-
-    @deprecated('[3.9] use entity.cw_has_perm(action)')
-    def has_perm(self, action):
-        return self.cw_has_perm(action)
-
-    @deprecated('[3.9] use entity.cw_set_relation_cache(rtype, role, rset)')
-    def set_related_cache(self, rtype, role, rset):
-        self.cw_set_relation_cache(rtype, role, rset)
-
-    @deprecated('[3.9] use entity.cw_clear_relation_cache(rtype, role)')
-    def clear_related_cache(self, rtype=None, role=None):
-        self.cw_clear_relation_cache(rtype, role)
-
-    @deprecated('[3.9] use entity.cw_related_rql(rtype, [role, [targettypes]])')
-    def related_rql(self, rtype, role='subject', targettypes=None):
-        return self.cw_related_rql(rtype, role, targettypes)
-
     @property
     @deprecated('[3.10] use entity.cw_edited')
     def edited_attributes(self):
--- a/etwist/twconfig.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/etwist/twconfig.py	Wed Oct 09 11:13:56 2013 +0200
@@ -34,9 +34,8 @@
 from cubicweb.web.webconfig import WebConfiguration
 
 
-class TwistedConfiguration(WebConfiguration):
+class WebConfigurationBase(WebConfiguration):
     """web instance (in a twisted web server) client of a RQL server"""
-    name = 'twisted'
 
     options = merge_options((
         # ctl configuration
@@ -107,19 +106,17 @@
         return 'http://%s:%s/' % (self['host'] or getfqdn(), self['port'] or 8080)
 
 
-CONFIGURATIONS.append(TwistedConfiguration)
-
 try:
     from cubicweb.server.serverconfig import ServerConfiguration
 
-    class AllInOneConfiguration(TwistedConfiguration, ServerConfiguration):
+    class AllInOneConfiguration(WebConfigurationBase, ServerConfiguration):
         """repository and web instance in the same twisted process"""
         name = 'all-in-one'
-        options = merge_options(TwistedConfiguration.options
+        options = merge_options(WebConfigurationBase.options
                                 + ServerConfiguration.options)
 
-        cubicweb_appobject_path = TwistedConfiguration.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
-        cube_appobject_path = TwistedConfiguration.cube_appobject_path | ServerConfiguration.cube_appobject_path
+        cubicweb_appobject_path = WebConfigurationBase.cubicweb_appobject_path | ServerConfiguration.cubicweb_appobject_path
+        cube_appobject_path = WebConfigurationBase.cube_appobject_path | ServerConfiguration.cube_appobject_path
         def pyro_enabled(self):
             """tell if pyro is activated for the in memory repository"""
             return self['pyro-server']
--- a/hooks/security.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/hooks/security.py	Wed Oct 09 11:13:56 2013 +0200
@@ -111,17 +111,11 @@
     events = ('after_update_entity',)
 
     def __call__(self):
-        try:
-            # check user has permission right now, if not retry at commit time
-            self.entity.cw_check_perm('update')
-            check_entity_attributes(self._cw, self.entity)
-        except Unauthorized:
-            self.entity._cw_clear_local_perm_cache('update')
-            # save back editedattrs in case the entity is reedited later in the
-            # same transaction, which will lead to cw_edited being
-            # overwritten
-            CheckEntityPermissionOp.get_instance(self._cw).add_data(
-                (self.entity.eid, 'update', self.entity.cw_edited) )
+        # save back editedattrs in case the entity is reedited later in the
+        # same transaction, which will lead to cw_edited being
+        # overwritten
+        CheckEntityPermissionOp.get_instance(self._cw).add_data(
+            (self.entity.eid, 'update', self.entity.cw_edited) )
 
 
 class BeforeDelEntitySecurityHook(SecurityHook):
--- a/interfaces.py	Mon Sep 30 18:07:51 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""Standard interfaces. Deprecated in favor of adapters.
-
-.. note::
-
-  The `implements` selector used to match not only entity classes but also their
-  interfaces. This will disappear in a future version. You should define an
-  adapter for that interface and use `adaptable('MyIFace')` selector on appobjects
-  that require that interface.
-
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common.interface import Interface
-
-
-# XXX deprecates in favor of IProgressAdapter
-class IProgress(Interface):
-    """something that has a cost, a state and a progression"""
-
-    @property
-    def cost(self):
-        """the total cost"""
-
-    @property
-    def done(self):
-        """what is already done"""
-
-    @property
-    def todo(self):
-        """what remains to be done"""
-
-    def progress_info(self):
-        """returns a dictionary describing progress/estimated cost of the
-        version.
-
-        - mandatory keys are (''estimated', 'done', 'todo')
-
-        - optional keys are ('notestimated', 'notestimatedcorrected',
-          'estimatedcorrected')
-
-        'noestimated' and 'notestimatedcorrected' should default to 0
-        'estimatedcorrected' should default to 'estimated'
-        """
-
-    def finished(self):
-        """returns True if status is finished"""
-
-    def in_progress(self):
-        """returns True if status is not finished"""
-
-    def progress(self):
-        """returns the % progress of the task item"""
-
-# XXX deprecates in favor of IMileStoneAdapter
-class IMileStone(IProgress):
-    """represents an ITask's item"""
-
-    parent_type = None # specify main task's type
-
-    def get_main_task(self):
-        """returns the main ITask entity"""
-
-    def initial_prevision_date(self):
-        """returns the initial expected end of the milestone"""
-
-    def eta_date(self):
-        """returns expected date of completion based on what remains
-        to be done
-        """
-
-    def completion_date(self):
-        """returns date on which the subtask has been completed"""
-
-    def contractors(self):
-        """returns the list of persons supposed to work on this task"""
-
-# XXX deprecates in favor of IEmbedableAdapter
-class IEmbedable(Interface):
-    """interface for embedable entities"""
-
-    def embeded_url(self):
-        """embed action interface"""
-
-# XXX deprecates in favor of ICalendarViewsAdapter
-class ICalendarViews(Interface):
-    """calendar views interface"""
-    def matching_dates(self, begin, end):
-        """
-        :param begin: day considered as begin of the range (`DateTime`)
-        :param end: day considered as end of the range (`DateTime`)
-
-        :return:
-          a list of dates (`DateTime`) in the range [`begin`, `end`] on which
-          this entity apply
-        """
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ICalendarable(Interface):
-    """interface for items that do have a begin date 'start' and an end date 'stop'
-    """
-
-    @property
-    def start(self):
-        """return start date"""
-
-    @property
-    def stop(self):
-        """return stop state"""
-
-# XXX deprecates in favor of ICalendarableAdapter
-class ITimetableViews(Interface):
-    """timetable views interface"""
-    def timetable_date(self):
-        """XXX explain
-
-        :return: date (`DateTime`)
-        """
-
-# XXX deprecates in favor of IGeocodableAdapter
-class IGeocodable(Interface):
-    """interface required by geocoding views such as gmap-view"""
-
-    @property
-    def latitude(self):
-        """returns the latitude of the entity"""
-
-    @property
-    def longitude(self):
-        """returns the longitude of the entity"""
-
-    def marker_icon(self):
-        """returns the icon that should be used as the marker"""
-
-
-# XXX deprecates in favor of IEmailableAdapter
-class IFeed(Interface):
-    """interface for entities with rss flux"""
-
-    def rss_feed_url(self):
-        """"""
-
-# XXX deprecates in favor of IDownloadableAdapter
-class IDownloadable(Interface):
-    """interface for downloadable entities"""
-
-    def download_url(self): # XXX not really part of this interface
-        """return an url to download entity's content"""
-    def download_content_type(self):
-        """return MIME type of the downloadable content"""
-    def download_encoding(self):
-        """return encoding of the downloadable content"""
-    def download_file_name(self):
-        """return file name of the downloadable content"""
-    def download_data(self):
-        """return actual data of the downloadable content"""
-
-# XXX deprecates in favor of IPrevNextAdapter
-class IPrevNext(Interface):
-    """interface for entities which can be linked to a previous and/or next
-    entity
-    """
-
-    def next_entity(self):
-        """return the 'next' entity"""
-    def previous_entity(self):
-        """return the 'previous' entity"""
-
-# XXX deprecates in favor of IBreadCrumbsAdapter
-class IBreadCrumbs(Interface):
-
-    def breadcrumbs(self, view, recurs=False):
-        pass
-
-# XXX deprecates in favor of ITreeAdapter
-class ITree(Interface):
-
-    def parent(self):
-        """returns the parent entity"""
-
-    def children(self):
-        """returns the item's children"""
-
-    def children_rql(self):
-        """XXX returns RQL to get children"""
-
-    def iterchildren(self):
-        """iterates over the item's children"""
-
-    def is_leaf(self):
-        """returns true if this node as no child"""
-
-    def is_root(self):
-        """returns true if this node has no parent"""
-
-    def root(self):
-        """returns the root object"""
-
--- a/mixins.py	Mon Sep 30 18:07:51 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""mixins of entity/views organized somewhat in a graph or tree structure"""
-__docformat__ = "restructuredtext en"
-
-from itertools import chain
-
-from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated, class_deprecated
-
-from cubicweb.predicates import implements
-from cubicweb.interfaces import ITree
-
-
-class TreeMixIn(object):
-    """base tree-mixin implementing the tree interface
-
-    This mixin has to be inherited explicitly and configured using the
-    tree_attribute, parent_target and children_target class attribute to
-    benefit from this default implementation
-    """
-    __metaclass__ = class_deprecated
-    __deprecation_warning__ = '[3.9] TreeMixIn is deprecated, use/override ITreeAdapter instead (%(cls)s)'
-
-    tree_attribute = None
-    # XXX misnamed
-    parent_target = 'subject'
-    children_target = 'object'
-
-    def different_type_children(self, entities=True):
-        """return children entities of different type as this entity.
-
-        according to the `entities` parameter, return entity objects or the
-        equivalent result set
-        """
-        res = self.related(self.tree_attribute, self.children_target,
-                           entities=entities)
-        if entities:
-            return [e for e in res if e.e_schema != self.e_schema]
-        return res.filtered_rset(lambda x: x.e_schema != self.e_schema, self.cw_col)
-
-    def same_type_children(self, entities=True):
-        """return children entities of the same type as this entity.
-
-        according to the `entities` parameter, return entity objects or the
-        equivalent result set
-        """
-        res = self.related(self.tree_attribute, self.children_target,
-                           entities=entities)
-        if entities:
-            return [e for e in res if e.e_schema == self.e_schema]
-        return res.filtered_rset(lambda x: x.e_schema is self.e_schema, self.cw_col)
-
-    def iterchildren(self, _done=None):
-        if _done is None:
-            _done = set()
-        for child in self.children():
-            if child.eid in _done:
-                self.error('loop in %s tree: %s', self.__regid__.lower(), child)
-                continue
-            yield child
-            _done.add(child.eid)
-
-    def prefixiter(self, _done=None):
-        if _done is None:
-            _done = set()
-        if self.eid in _done:
-            return
-        _done.add(self.eid)
-        yield self
-        for child in self.same_type_children():
-            for entity in child.prefixiter(_done):
-                yield entity
-
-    @cached
-    def path(self):
-        """returns the list of eids from the root object to this object"""
-        path = []
-        parent = self
-        while parent:
-            if parent.eid in path:
-                self.error('loop in %s tree: %s', self.__regid__.lower(), parent)
-                break
-            path.append(parent.eid)
-            try:
-                # check we are not leaving the tree
-                if (parent.tree_attribute != self.tree_attribute or
-                    parent.parent_target != self.parent_target):
-                    break
-                parent = parent.parent()
-            except AttributeError:
-                break
-
-        path.reverse()
-        return path
-
-    def iterparents(self, strict=True):
-        def _uptoroot(self):
-            curr = self
-            while True:
-                curr = curr.parent()
-                if curr is None:
-                    break
-                yield curr
-        if not strict:
-            return chain([self], _uptoroot(self))
-        return _uptoroot(self)
-
-    ## ITree interface ########################################################
-    def parent(self):
-        """return the parent entity if any, else None (e.g. if we are on the
-        root
-        """
-        try:
-            return self.related(self.tree_attribute, self.parent_target,
-                                entities=True)[0]
-        except (KeyError, IndexError):
-            return None
-
-    def children(self, entities=True, sametype=False):
-        """return children entities
-
-        according to the `entities` parameter, return entity objects or the
-        equivalent result set
-        """
-        if sametype:
-            return self.same_type_children(entities)
-        else:
-            return self.related(self.tree_attribute, self.children_target,
-                                entities=entities)
-
-    def children_rql(self):
-        return self.cw_related_rql(self.tree_attribute, self.children_target)
-
-    def is_leaf(self):
-        return len(self.children()) == 0
-
-    def is_root(self):
-        return self.parent() is None
-
-    def root(self):
-        """return the root object"""
-        return self._cw.entity_from_eid(self.path()[0])
-
-
-class EmailableMixIn(object):
-    """base mixin providing the default get_email() method used by
-    the massmailing view
-
-    NOTE: The default implementation is based on the
-    primary_email / use_email scheme
-    """
-    @deprecated("[3.9] use entity.cw_adapt_to('IEmailable').get_email()")
-    def get_email(self):
-        if getattr(self, 'primary_email', None):
-            return self.primary_email[0].address
-        if getattr(self, 'use_email', None):
-            return self.use_email[0].address
-        return None
-
-
-"""pluggable mixins system: plug classes registered in MI_REL_TRIGGERS on entity
-classes which have the relation described by the dict's key.
-
-NOTE: pluggable mixins can't override any method of the 'explicit' user classes tree
-(eg without plugged classes). This includes bases Entity and AnyEntity classes.
-"""
-MI_REL_TRIGGERS = {
-    ('primary_email',   'subject'): EmailableMixIn,
-    ('use_email',   'subject'): EmailableMixIn,
-    }
-
-
-# XXX move to cubicweb.web.views.treeview once we delete usage from this file
-def _done_init(done, view, row, col):
-    """handle an infinite recursion safety belt"""
-    if done is None:
-        done = set()
-    entity = view.cw_rset.get_entity(row, col)
-    if entity.eid in done:
-        msg = entity._cw._('loop in %(rel)s relation (%(eid)s)') % {
-            'rel': entity.cw_adapt_to('ITree').tree_relation,
-            'eid': entity.eid
-            }
-        return None, msg
-    done.add(entity.eid)
-    return done, entity
-
-
-class TreeViewMixIn(object):
-    """a recursive tree view"""
-    __metaclass__ = class_deprecated
-    __deprecation_warning__ = '[3.9] TreeViewMixIn is deprecated, use/override BaseTreeView instead (%(cls)s)'
-
-    __regid__ = 'tree'
-    __select__ = implements(ITree, warn=False)
-    item_vid = 'treeitem'
-
-    def call(self, done=None, **kwargs):
-        if done is None:
-            done = set()
-        super(TreeViewMixIn, self).call(done=done, **kwargs)
-
-    def cell_call(self, row, col=0, vid=None, done=None, maxlevel=None, **kwargs):
-        assert maxlevel is None or maxlevel > 0
-        done, entity = _done_init(done, self, row, col)
-        if done is None:
-            # entity is actually an error message
-            self.w(u'<li class="badcontent">%s</li>' % entity)
-            return
-        self.open_item(entity)
-        entity.view(vid or self.item_vid, w=self.w, **kwargs)
-        if maxlevel is not None:
-            maxlevel -= 1
-            if maxlevel == 0:
-                self.close_item(entity)
-                return
-        relatedrset = entity.children(entities=False)
-        self.wview(self.__regid__, relatedrset, 'null', done=done,
-                   maxlevel=maxlevel, **kwargs)
-        self.close_item(entity)
-
-    def open_item(self, entity):
-        self.w(u'<li class="%s">\n' % entity.cw_etype.lower())
-    def close_item(self, entity):
-        self.w(u'</li>\n')
-
-
-class TreePathMixIn(object):
-    """a recursive path view"""
-    __metaclass__ = class_deprecated
-    __deprecation_warning__ = '[3.9] TreePathMixIn is deprecated, use/override TreePathView instead (%(cls)s)'
-    __regid__ = 'path'
-    item_vid = 'oneline'
-    separator = u'&#160;&gt;&#160;'
-
-    def call(self, **kwargs):
-        self.w(u'<div class="pathbar">')
-        super(TreePathMixIn, self).call(**kwargs)
-        self.w(u'</div>')
-
-    def cell_call(self, row, col=0, vid=None, done=None, **kwargs):
-        done, entity = _done_init(done, self, row, col)
-        if done is None:
-            # entity is actually an error message
-            self.w(u'<span class="badcontent">%s</span>' % entity)
-            return
-        parent = entity.parent()
-        if parent:
-            parent.view(self.__regid__, w=self.w, done=done)
-            self.w(self.separator)
-        entity.view(vid or self.item_vid, w=self.w)
-
-
-class ProgressMixIn(object):
-    """provide a default implementations for IProgress interface methods"""
-    __metaclass__ = class_deprecated
-    __deprecation_warning__ = '[3.9] ProgressMixIn is deprecated, use/override IProgressAdapter instead (%(cls)s)'
-
-    @property
-    def cost(self):
-        return self.progress_info()['estimated']
-
-    @property
-    def revised_cost(self):
-        return self.progress_info().get('estimatedcorrected', self.cost)
-
-    @property
-    def done(self):
-        return self.progress_info()['done']
-
-    @property
-    def todo(self):
-        return self.progress_info()['todo']
-
-    @cached
-    def progress_info(self):
-        raise NotImplementedError()
-
-    def finished(self):
-        return not self.in_progress()
-
-    def in_progress(self):
-        raise NotImplementedError()
-
-    def progress(self):
-        try:
-            return 100. * self.done / self.revised_cost
-        except ZeroDivisionError:
-            # total cost is 0 : if everything was estimated, task is completed
-            if self.progress_info().get('notestimated'):
-                return 0.
-            return 100
--- a/predicates.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/predicates.py	Wed Oct 09 11:13:56 2013 +0200
@@ -204,27 +204,6 @@
 # remember, these imports are there for bw compat only
 __BACKWARD_COMPAT_IMPORTS = (yes,)
 
-def score_interface(etypesreg, eclass, iface):
-    """Return XXX if the give object (maybe an instance or class) implements
-    the interface.
-    """
-    if getattr(iface, '__registry__', None) == 'etypes':
-        # adjust score if the interface is an entity class
-        parents, any = etypesreg.parent_classes(eclass.__regid__)
-        if iface is eclass:
-            return len(parents) + 4
-        if iface is any: # Any
-            return 1
-        for index, basecls in enumerate(reversed(parents)):
-            if iface is basecls:
-                return index + 3
-        return 0
-    # XXX iface in implements deprecated in 3.9
-    if implements_iface(eclass, iface):
-        # implementing an interface takes precedence other special Any interface
-        return 2
-    return 0
-
 
 # abstract predicates / mixin helpers ###########################################
 
@@ -745,53 +724,6 @@
         return 1 # necessarily true if we're there
 
 
-class implements(EClassPredicate):
-    """Return non-zero score for entity that are of the given type(s) or
-    implements at least one of the given interface(s). If multiple arguments are
-    given, matching one of them is enough.
-
-    Entity types should be given as string, the corresponding class will be
-    fetched from the entity types registry at selection time.
-
-    See :class:`~cubicweb.predicates.EClassPredicate` documentation for entity
-    class lookup / score rules according to the input context.
-
-    .. note::
-
-       when interface is an entity class, the score will reflect class
-       proximity so the most specific object will be selected.
-
-    .. note::
-
-       deprecated in cubicweb >= 3.9, use either
-       :class:`~cubicweb.predicates.is_instance` or
-       :class:`~cubicweb.predicates.adaptable`.
-    """
-
-    def __init__(self, *expected_ifaces, **kwargs):
-        emit_warn = kwargs.pop('warn', True)
-        super(implements, self).__init__(**kwargs)
-        self.expected_ifaces = expected_ifaces
-        if emit_warn:
-            warn('[3.9] implements predicate is deprecated, use either '
-                 'is_instance or adaptable', DeprecationWarning, stacklevel=2)
-
-    def __str__(self):
-        return '%s(%s)' % (self.__class__.__name__,
-                           ','.join(str(s) for s in self.expected_ifaces))
-
-    def score_class(self, eclass, req):
-        score = 0
-        etypesreg = req.vreg['etypes']
-        for iface in self.expected_ifaces:
-            if isinstance(iface, basestring):
-                # entity type
-                try:
-                    iface = etypesreg.etype_class(iface)
-                except KeyError:
-                    continue # entity type not in the schema
-            score += score_interface(etypesreg, eclass, iface)
-        return score
 
 def _reset_is_instance_cache(vreg):
     vreg._is_instance_predicate_cache = {}
--- a/schema.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/schema.py	Wed Oct 09 11:13:56 2013 +0200
@@ -982,14 +982,11 @@
     distinct_query = None
 
     def serialize(self):
-        # start with a comma for bw compat,see below
+        # start with a semicolon for bw compat, see below
         return ';' + ','.join(sorted(self.mainvars)) + ';' + self.expression
 
     @classmethod
     def deserialize(cls, value):
-        # XXX < 3.5.10 bw compat
-        if not value.startswith(';'):
-            return cls(value)
         _, mainvars, expression = value.split(';', 2)
         return cls(expression, mainvars)
 
@@ -1044,9 +1041,6 @@
                                self.msg or '')
 
     def deserialize(cls, value):
-        # XXX < 3.5.10 bw compat
-        if not value.startswith(';'):
-            return cls(value)
         value, msg = value.split('\n', 1)
         _, mainvars, expression = value.split(';', 2)
         return cls(expression, mainvars, msg)
--- a/server/hook.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/server/hook.py	Wed Oct 09 11:13:56 2013 +0200
@@ -673,16 +673,6 @@
                         {'x': self.eidfrom, 'p': self.eidto})
 
 
-PropagateSubjectRelationHook = class_renamed(
-    'PropagateSubjectRelationHook', PropagateRelationHook,
-    '[3.9] PropagateSubjectRelationHook has been renamed to PropagateRelationHook')
-PropagateSubjectRelationAddHook = class_renamed(
-    'PropagateSubjectRelationAddHook', PropagateRelationAddHook,
-    '[3.9] PropagateSubjectRelationAddHook has been renamed to PropagateRelationAddHook')
-PropagateSubjectRelationDelHook = class_renamed(
-    'PropagateSubjectRelationDelHook', PropagateRelationDelHook,
-    '[3.9] PropagateSubjectRelationDelHook has been renamed to PropagateRelationDelHook')
-
 
 # abstract classes for operation ###############################################
 
--- a/server/migractions.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/server/migractions.py	Wed Oct 09 11:13:56 2013 +0200
@@ -1461,12 +1461,9 @@
                 # no result to fetch
                 return
 
-    def rqlexec(self, rql, kwargs=None, cachekey=None, build_descr=True,
+    def rqlexec(self, rql, kwargs=None, build_descr=True,
                 ask_confirm=False):
         """rql action"""
-        if cachekey is not None:
-            warn('[3.8] cachekey is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
         if not isinstance(rql, (tuple, list)):
             rql = ( (rql, kwargs), )
         res = None
--- a/server/schemaserial.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/server/schemaserial.py	Wed Oct 09 11:13:56 2013 +0200
@@ -77,8 +77,6 @@
 def cstrtype_mapping(cursor):
     """cached constraint types mapping"""
     map = dict(cursor.execute('Any T, X WHERE X is CWConstraintType, X name T'))
-    if not 'BoundConstraint' in map:
-        map['BoundConstraint'] = map['BoundaryConstraint']
     return map
 
 # schema / perms deserialization ##############################################
@@ -234,28 +232,24 @@
         if rdefs is not None:
             set_perms(rdefs, permsidx)
     unique_togethers = {}
-    try:
-        rset = session.execute(
-        'Any X,E,R WHERE '
-        'X is CWUniqueTogetherConstraint, '
-        'X constraint_of E, X relations R', build_descr=False)
-    except Exception:
-        session.rollback() # first migration introducing CWUniqueTogetherConstraint cw 3.9.6
-    else:
-        for values in rset:
-            uniquecstreid, eeid, releid = values
-            eschema = schema.schema_by_eid(eeid)
-            relations = unique_togethers.setdefault(uniquecstreid, (eschema, []))
-            rel = ertidx[releid]
-            if isinstance(rel, schemamod.RelationDefinitionSchema):
-                # not yet migrated 3.9 database ('relations' target type changed
-                # to CWRType in 3.10)
-                rtype = rel.rtype.type
-            else:
-                rtype = str(rel)
-            relations[1].append(rtype)
-        for eschema, unique_together in unique_togethers.itervalues():
-            eschema._unique_together.append(tuple(sorted(unique_together)))
+    rset = session.execute(
+    'Any X,E,R WHERE '
+    'X is CWUniqueTogetherConstraint, '
+    'X constraint_of E, X relations R', build_descr=False)
+    for values in rset:
+        uniquecstreid, eeid, releid = values
+        eschema = schema.schema_by_eid(eeid)
+        relations = unique_togethers.setdefault(uniquecstreid, (eschema, []))
+        rel = ertidx[releid]
+        if isinstance(rel, schemamod.RelationDefinitionSchema):
+            # not yet migrated 3.9 database ('relations' target type changed
+            # to CWRType in 3.10)
+            rtype = rel.rtype.type
+        else:
+            rtype = str(rel)
+        relations[1].append(rtype)
+    for eschema, unique_together in unique_togethers.itervalues():
+        eschema._unique_together.append(tuple(sorted(unique_together)))
     schema.infer_specialization_rules()
     session.commit()
     schema.reading_from_database = False
@@ -344,13 +338,10 @@
     cstrtypemap = {}
     rql = 'INSERT CWConstraintType X: X name %(ct)s'
     for cstrtype in CONSTRAINTS:
-        if cstrtype == 'BoundConstraint':
-            continue # XXX deprecated in yams 0.29 / cw 3.8.1
         cstrtypemap[cstrtype] = execute(rql, {'ct': unicode(cstrtype)},
                                         build_descr=False)[0][0]
         if pb is not None:
             pb.update()
-    cstrtypemap['BoundConstraint'] = cstrtypemap['BoundaryConstraint']
     # serialize relations
     for rschema in schema.relations():
         # skip virtual relations such as eid, has_text and identity
--- a/server/session.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/server/session.py	Wed Oct 09 11:13:56 2013 +0200
@@ -1176,14 +1176,11 @@
     source_from_eid = tx_meth('source_from_eid')
 
 
-    def execute(self, rql, kwargs=None, eid_key=None, build_descr=True):
+    def execute(self, rql, kwargs=None, build_descr=True):
         """db-api like method directly linked to the querier execute method.
 
         See :meth:`cubicweb.dbapi.Cursor.execute` documentation.
         """
-        if eid_key is not None:
-            warn('[3.8] eid_key is deprecated, you can safely remove this argument',
-                 DeprecationWarning, stacklevel=2)
         self.timestamp = time() # update timestamp
         rset = self._execute(self, rql, kwargs, build_descr)
         rset.req = self
--- a/test/data/migration/0.1.0_web.py	Mon Sep 30 18:07:51 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""web only
-
-"""
--- a/test/unittest_entity.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/test/unittest_entity.py	Wed Oct 09 11:13:56 2013 +0200
@@ -742,7 +742,7 @@
         self.assertEqual(card.absolute_url(),
                           'http://testing.fr/cubicweb/%s' % card.eid)
 
-    def test_create_entity(self):
+    def test_create_and_compare_entity(self):
         req = self.request()
         p1 = req.create_entity('Personne', nom=u'fayolle', prenom=u'alexandre')
         p2 = req.create_entity('Personne', nom=u'campeas', prenom=u'aurelien')
@@ -756,6 +756,15 @@
         self.assertEqual(sorted([c.nom for c in p.evaluee]), ['campeas', 'fayolle'])
         self.assertEqual([c.type for c in p.reverse_ecrit_par], ['z'])
 
+        req = self.request()
+        auc = req.execute('Personne P WHERE P prenom "aurelien"').get_entity(0,0)
+        persons = set()
+        persons.add(p1)
+        persons.add(p2)
+        persons.add(auc)
+        self.assertEqual(2, len(persons))
+        self.assertNotEqual(p1, p2)
+        self.assertEqual(p2, auc)
 
 
 if __name__ == '__main__':
--- a/test/unittest_migration.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/test/unittest_migration.py	Wed Oct 09 11:13:56 2013 +0200
@@ -79,10 +79,6 @@
         self.assert_(not isinstance(config.migration_handler(), ServerMigrationHelper))
         self.assertIsInstance(config.migration_handler(), MigrationHelper)
         config = self.config
-        config.__class__.name = 'twisted'
-        self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
-                              [((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
-                               ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
         config.__class__.name = 'repository'
         self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
                               [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
@@ -92,8 +88,7 @@
         self.assertListEqual(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)),
                               [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'),
                                ((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'),
-                               ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py'),
-                               ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')])
+                               ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py')])
         config.__class__.name = 'repository'
 
 
--- a/view.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/view.py	Wed Oct 09 11:13:56 2013 +0200
@@ -558,34 +558,6 @@
     __registry__ = 'adapters'
 
 
-def implements_adapter_compat(iface):
-    def _pre39_compat(func):
-        def decorated(self, *args, **kwargs):
-            entity = self.entity
-            if hasattr(entity, func.__name__):
-                warn('[3.9] %s method is deprecated, define it on a custom '
-                     '%s for %s instead' % (func.__name__, iface,
-                                            entity.__class__),
-                     DeprecationWarning)
-                member = getattr(entity, func.__name__)
-                if callable(member):
-                    return member(*args, **kwargs)
-                return member
-            return func(self, *args, **kwargs)
-        decorated.decorated = func
-        return decorated
-    return _pre39_compat
-
-
-def unwrap_adapter_compat(cls):
-    parent = cls.__bases__[0]
-    for member_name in dir(parent):
-        member = getattr(parent, member_name)
-        if isinstance(member, types.MethodType) and hasattr(member.im_func, 'decorated') and not member_name in cls.__dict__:
-            method = new.instancemethod(member.im_func.decorated, None, cls)
-            setattr(cls, member_name, method)
-
-
 class auto_unwrap_bw_compat(type):
     def __new__(mcs, name, bases, classdict):
         cls = type.__new__(mcs, name, bases, classdict)
@@ -596,7 +568,6 @@
 
 class EntityAdapter(Adapter):
     """base class for entity adapters (eg adapt an entity to an interface)"""
-    __metaclass__ = auto_unwrap_bw_compat
     def __init__(self, _cw, **kwargs):
         try:
             self.entity = kwargs.pop('entity')
--- a/web/__init__.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/__init__.py	Wed Oct 09 11:13:56 2013 +0200
@@ -31,8 +31,6 @@
 from cubicweb.uilib import eid_param
 
 assert json_dumps is not None, 'no json module installed'
-dumps = deprecated('[3.9] use cubicweb.utils.json_dumps instead of dumps')(
-    json_dumps)
 
 INTERNAL_FIELD_VALUE = '__cubicweb_internal_field__'
 
--- a/web/data/cubicweb.ajax.js	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/data/cubicweb.ajax.js	Wed Oct 09 11:13:56 2013 +0200
@@ -339,11 +339,6 @@
         cw.log('loadxhtml called without an element');
     }
     var callback = null;
-    if (form && form.callback) {
-        cw.log('[3.9] callback given through form.callback is deprecated, add ' + 'callback on the defered');
-        callback = form.callback;
-        delete form.callback;
-    }
     var node = this.get(0); // only consider the first element
     if (cursor) {
         setProgressCursor();
@@ -734,13 +729,7 @@
 
 /* DEPRECATED *****************************************************************/
 
-preprocessAjaxLoad = cw.utils.deprecatedFunction(
-    '[3.9] preprocessAjaxLoad() is deprecated, use loadAjaxHtmlHead instead',
-    function(node, newdomnode) {
-        return loadAjaxHtmlHead(newdomnode);
-    }
-);
-
+// still used in cwo and keyword cubes at least
 reloadComponent = cw.utils.deprecatedFunction(
     '[3.9] reloadComponent() is deprecated, use loadxhtml instead',
     function(compid, rql, registry, nodeid, extraargs) {
@@ -754,52 +743,6 @@
     }
 );
 
-reloadBox = cw.utils.deprecatedFunction(
-    '[3.9] reloadBox() is deprecated, use loadxhtml instead',
-    function(boxid, rql) {
-        return reloadComponent(boxid, rql, 'ctxcomponents', boxid);
-    }
-);
-
-replacePageChunk = cw.utils.deprecatedFunction(
-    '[3.9] replacePageChunk() is deprecated, use loadxhtml instead',
-    function(nodeId, rql, vid, extraparams, /* ... */ swap, callback) {
-        var params = null;
-        if (callback) {
-            params = {
-                callback: callback
-            };
-        }
-        var node = jQuery('#' + nodeId)[0];
-        var props = {};
-        if (node) {
-            props['rql'] = rql;
-            props['fname'] = 'view';
-            props['pageid'] = pageid;
-            if (vid) {
-                props['vid'] = vid;
-            }
-            if (extraparams) {
-                jQuery.extend(props, extraparams);
-            }
-            // FIXME we need to do asURL(props) manually instead of
-            // passing `props` directly to loadxml because replacePageChunk
-            // is sometimes called (abusively) with some extra parameters in `vid`
-            var mode = swap ? 'swap': 'replace';
-            var url = AJAX_BASE_URL + asURL(props);
-            jQuery(node).loadxhtml(url, params, 'get', mode);
-        } else {
-            cw.log('Node', nodeId, 'not found');
-        }
-    }
-);
-
-loadxhtml = cw.utils.deprecatedFunction(
-    '[3.9] loadxhtml() function is deprecated, use loadxhtml method instead',
-    function(nodeid, url, /* ... */ replacemode) {
-        jQuery('#' + nodeid).loadxhtml(url, null, 'post', replacemode);
-    }
-);
 
 function remoteExec(fname /* ... */) {
     setProgressCursor();
--- a/web/data/cubicweb.compat.js	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/data/cubicweb.compat.js	Wed Oct 09 11:13:56 2013 +0200
@@ -1,34 +1,3 @@
-cw.utils.movedToNamespace(['log', 'jqNode', 'getNode', 'evalJSON', 'urlEncode',
-                           'swapDOM'], cw);
-cw.utils.movedToNamespace(['nodeWalkDepthFirst', 'formContents', 'isArray',
-                           'isString', 'isArrayLike', 'sliceList',
-                           'toISOTimestamp'], cw.utils);
-
-
-if ($.noop === undefined) {
-    function noop() {}
-} else {
-    noop = cw.utils.deprecatedFunction(
-        '[3.9] noop() is deprecated, use $.noop() instead (XXX requires jQuery 1.4)',
-        $.noop);
-}
-
-// ========== ARRAY EXTENSIONS ========== ///
-Array.prototype.contains = cw.utils.deprecatedFunction(
-    '[3.9] array.contains(elt) is deprecated, use $.inArray(elt, array)!=-1 instead',
-    function(element) {
-        return jQuery.inArray(element, this) != - 1;
-    }
-);
-
-// ========== END OF ARRAY EXTENSIONS ========== ///
-forEach = cw.utils.deprecatedFunction(
-    '[3.9] forEach() is deprecated, use $.each() instead',
-    function(array, func) {
-        return $.each(array, func);
-    }
-);
-
 /**
  * .. function:: cw.utils.deprecatedFunction(msg, function)
  *
@@ -41,64 +10,20 @@
  *   [ ["a", "b", "c"], ["d", "e"] ]
  */
  // XXX why not the same argument order as $.map and forEach ?
-map = cw.utils.deprecatedFunction(
-    '[3.9] map() is deprecated, use $.map instead',
-    function(func, array) {
-        var result = [];
-        for (var i = 0, length = array.length; i < length; i++) {
-            result.push(func(array[i]));
-        }
-        return result;
-    }
-);
 
-findValue = cw.utils.deprecatedFunction(
-    '[3.9] findValue(array, elt) is deprecated, use $.inArray(elt, array) instead',
-    function(array, element) {
-        return jQuery.inArray(element, array);
-    }
-);
-
-filter = cw.utils.deprecatedFunction(
-    '[3.9] filter(func, array) is deprecated, use $.grep(array, f) instead',
-    function(func, array) {
-        return $.grep(array, func);
+function map(func, array) {
+    var result = [];
+    for (var i = 0, length = array.length; i < length; i++) {
+        result.push(func(array[i]));
     }
-);
-
-addElementClass = cw.utils.deprecatedFunction(
-    '[3.9] addElementClass(node, cls) is deprecated, use $(node).addClass(cls) instead',
-    function(node, klass) {
-        $(node).addClass(klass);
-    }
-);
+    return result;
+}
 
-removeElementClass = cw.utils.deprecatedFunction(
-    '[3.9] removeElementClass(node, cls) is deprecated, use $(node).removeClass(cls) instead',
-    function(node, klass) {
-        $(node).removeClass(klass);
-    }
-);
 
-hasElementClass = cw.utils.deprecatedFunction(
-    '[3.9] hasElementClass(node, cls) is deprecated, use $(node).hasClass(cls)',
-    function(node, klass) {
-        return $(node).hasClass(klass);
-    }
-);
-
+// skm cube still uses this
 getNodeAttribute = cw.utils.deprecatedFunction(
     '[3.9] getNodeAttribute(node, attr) is deprecated, use $(node).attr(attr)',
     function(node, attribute) {
         return $(node).attr(attribute);
     }
 );
-
-/**
- * The only known usage of KEYS is in the tag cube. Once cubicweb-tag 1.7.0 is out,
- * this current definition can be removed.
- */
-var KEYS = {
-    KEY_ESC: 27,
-    KEY_ENTER: 13
-};
--- a/web/data/cubicweb.htmlhelpers.js	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/data/cubicweb.htmlhelpers.js	Wed Oct 09 11:13:56 2013 +0200
@@ -78,10 +78,10 @@
         // generate a list of couple key=value if key is multivalued
         if (cw.utils.isArrayLike(value)) {
             for (var i = 0; i < value.length; i++) {
-                chunks.push(key + '=' + urlEncode(value[i]));
+                chunks.push(key + '=' + cw.urlEncode(value[i]));
             }
         } else {
-            chunks.push(key + '=' + urlEncode(value));
+            chunks.push(key + '=' + cw.urlEncode(value));
         }
     }
     return chunks.join('&');
--- a/web/data/cubicweb.js	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/data/cubicweb.js	Wed Oct 09 11:13:56 2013 +0200
@@ -105,15 +105,6 @@
         };
     },
 
-    movedToNamespace: function (funcnames, namespace) {
-        for (var i = 0; i < funcnames.length; i++) {
-            var funcname = funcnames[i];
-            var msg = ('[3.9] ' + funcname + ' is deprecated, use ' +
-		       namespace.__name__ + '.' + funcname + ' instead');
-            window[funcname] = cw.utils.deprecatedFunction(msg, namespace[funcname]);
-        }
-    },
-
     createDomFunction: function (tag) {
         function builddom(params, children) {
             var node = document.createElement(tag);
@@ -388,14 +379,6 @@
 
 });
 
-String.prototype.startsWith = cw.utils.deprecatedFunction('[3.9] str.startsWith() is deprecated, use str.startswith() instead', function (prefix) {
-    return this.startswith(prefix);
-});
-
-String.prototype.endsWith = cw.utils.deprecatedFunction('[3.9] str.endsWith() is deprecated, use str.endswith() instead', function (suffix) {
-    return this.endswith(prefix);
-});
-
 /** DOM factories ************************************************************/
 A = cw.utils.createDomFunction('a');
 BUTTON = cw.utils.createDomFunction('button');
@@ -472,7 +455,8 @@
     return node;
 }
 
-// XXX avoid crashes / backward compat
+// cubes: tag, keyword and apycot seem to use this, including require/provide
+// backward compat
 CubicWeb = cw;
 
 jQuery.extend(cw, {
--- a/web/formfields.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/formfields.py	Wed Oct 09 11:13:56 2013 +0200
@@ -404,17 +404,6 @@
                      for label, value in vocab]
         if self.sort:
             vocab = vocab_sort(vocab)
-        # XXX pre 3.9 bw compat
-        for i, option in enumerate(vocab):
-            # option may be a 2 or 3-uple (see Select widget _render method for
-            # explanation)
-            value = option[1]
-            if value is not None and not isinstance(value, basestring):
-                warn('[3.9] %s: vocabulary value should be an unicode string'
-                     % self, DeprecationWarning)
-                option = list(option)
-                option[1] = unicode(value)
-                vocab[i] = option
         return vocab
 
     # support field as argument to avoid warning when used as format field value
--- a/web/formwidgets.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/formwidgets.py	Wed Oct 09 11:13:56 2013 +0200
@@ -588,13 +588,7 @@
     def _render(self, form, field, renderer):
         curvalues, attrs = self.values_and_attributes(form, field)
         domid = attrs.pop('id', None)
-        # XXX turn this as initializer argument
-        try:
-            sep = attrs.pop('separator')
-            warn('[3.8] separator should be specified using initializer argument',
-                 DeprecationWarning)
-        except KeyError:
-            sep = self.separator
+        sep = self.separator
         options = []
         for i, option in enumerate(field.vocabulary(form)):
             try:
@@ -688,7 +682,7 @@
         # XXX find a way to understand every format
         fmt = req.property_value('ui.date-format')
         fmt = fmt.replace('%Y', 'yy').replace('%m', 'mm').replace('%d', 'dd')
-        req.add_onload(u'$("#%s").datepicker('
+        req.add_onload(u'cw.jqNode("%s").datepicker('
                        '{buttonImage: "%s", dateFormat: "%s", firstDay: 1,'
                        ' showOn: "button", buttonImageOnly: true})' % (
                            domid, req.uiprops['CALENDAR_ICON'], fmt))
@@ -720,7 +714,7 @@
 
     def _render(self, form, field, renderer):
         domid = field.dom_id(form, self.suffix)
-        form._cw.add_onload(u'$("#%s").timePicker({step: %s, separator: "%s"})' % (
+        form._cw.add_onload(u'cw.jqNode("%s").timePicker({step: %s, separator: "%s"})' % (
                 domid, self.timesteps, self.separator))
         return self._render_input(form, field)
 
--- a/web/request.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/request.py	Wed Oct 09 11:13:56 2013 +0200
@@ -684,12 +684,6 @@
                 cssfile = self.data_url(cssfile)
             add_css(cssfile, media, *extraargs)
 
-    @deprecated('[3.9] use ajax_replace_url() instead, naming rql and vid arguments')
-    def build_ajax_replace_url(self, nodeid, rql, vid, replacemode='replace',
-                               **extraparams):
-        return self.ajax_replace_url(nodeid, replacemode, rql=rql, vid=vid,
-                                     **extraparams)
-
     def ajax_replace_url(self, nodeid, replacemode='replace', **extraparams):
         """builds an ajax url that will replace nodeid's content
 
@@ -992,20 +986,6 @@
     def html_content_type(self):
         return 'text/html'
 
-    @deprecated('[3.9] use req.uiprops[rid]')
-    def external_resource(self, rid, default=_MARKER):
-        """return a path to an external resource, using its identifier
-
-        raise `KeyError` if the resource is not defined
-        """
-        try:
-            return self.uiprops[rid]
-        except KeyError:
-            if default is _MARKER:
-                raise
-            return default
-
-
 
 ## HTTP-accept parsers / utilies ##############################################
 def _mimetype_sort_key(accept_info):
--- a/web/test/jstests/test_utils.js	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/test/jstests/test_utils.js	Wed Oct 09 11:13:56 2013 +0200
@@ -47,11 +47,11 @@
   module("sliceList");
   test("test slicelist", function() {
       var list = ['a', 'b', 'c', 'd', 'e', 'f'];
-      same(sliceList(list, 2),  ['c', 'd', 'e', 'f']);
-      same(sliceList(list, 2, -2), ['c', 'd']);
-      same(sliceList(list, -3), ['d', 'e', 'f']);
-      same(sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
-      same(sliceList(list),  list);
+      same(cw.utils.sliceList(list, 2),  ['c', 'd', 'e', 'f']);
+      same(cw.utils.sliceList(list, 2, -2), ['c', 'd']);
+      same(cw.utils.sliceList(list, -3), ['d', 'e', 'f']);
+      same(cw.utils.sliceList(list, 0, -2), ['a', 'b', 'c', 'd']);
+      same(cw.utils.sliceList(list),  list);
   });
 
   module("formContents", {
@@ -83,7 +83,7 @@
 			     'value="one" />');
       $('#test-form').append('<input name="unchecked-choice" type="radio" ' +
 			     'value="two"/>');
-      same(formContents($('#test-form')[0]), [
+      same(cw.utils.formContents($('#test-form')[0]), [
 	['input-text', 'mytextarea', 'choice', 'check', 'theselect'],
 	['toto', 'Hello World!', 'no', 'no', 'foo']
       ]);
--- a/web/views/autoform.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/autoform.py	Wed Oct 09 11:13:56 2013 +0200
@@ -726,16 +726,17 @@
     # action on the form tag
     _default_form_action_path = 'validateform'
 
-    # pre 3.8.3 compat
+    @deprecated('[3.18] you should override form_action()')
     def set_action(self, action):
         self._action = action
+
+    @deprecated('[3.18] use form_action()')
     def get_action(self):
         try:
             return self._action
         except AttributeError:
             return self._cw.build_url(self._default_form_action_path)
-    action = property(deprecated('[3.9] use form.form_action()')(get_action),
-                      set_action)
+
 
     @iclassmethod
     def field_by_name(cls_or_self, name, role=None, eschema=None):
--- a/web/views/baseviews.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/baseviews.py	Wed Oct 09 11:13:56 2013 +0200
@@ -424,11 +424,7 @@
     redirect_vid = 'incontext'
 
     def call(self, subvid=None, **kwargs):
-        if subvid is None and 'vid' in kwargs:
-            warn("[3.9] should give a 'subvid' argument instead of 'vid'",
-                 DeprecationWarning, stacklevel=2)
-        else:
-            kwargs['vid'] = subvid
+        kwargs['vid'] = subvid
         rset = self.cw_rset
         for i in xrange(len(rset)):
             self.cell_call(i, 0, **kwargs)
@@ -643,14 +639,3 @@
 RssView = class_moved(xmlrss.RSSView)
 RssItemView = class_moved(xmlrss.RSSItemView)
 TableView = class_moved(tableview.TableView)
-
-
-class SecondaryView(EntityView):
-    __metaclass__ = class_deprecated
-    __deprecation_warning__ = '[3.9] the secondary view is deprecated, use one of oneline/incontext/outofcontext'
-    __regid__ = 'secondary'
-
-    def cell_call(self, row, col, **kwargs):
-        entity = self.cw_rset.get_entity(row, col)
-        self.w(u'&#160;')
-        self.wview('oneline', self.cw_rset, row=row, col=col)
--- a/web/views/calendar.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/calendar.py	Wed Oct 09 11:13:56 2013 +0200
@@ -27,9 +27,8 @@
 from logilab.common.date import todatetime
 
 from cubicweb.utils import json_dumps, make_uid
-from cubicweb.interfaces import ICalendarable
-from cubicweb.predicates import implements, adaptable
-from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat
+from cubicweb.predicates import adaptable
+from cubicweb.view import EntityView, EntityAdapter
 
 # useful constants & functions ################################################
 
@@ -46,16 +45,14 @@
 class ICalendarableAdapter(EntityAdapter):
     __needs_bw_compat__ = True
     __regid__ = 'ICalendarable'
-    __select__ = implements(ICalendarable, warn=False) # XXX for bw compat, should be abstract
+    __abstract__ = True
 
     @property
-    @implements_adapter_compat('ICalendarable')
     def start(self):
         """return start date"""
         raise NotImplementedError
 
     @property
-    @implements_adapter_compat('ICalendarable')
     def stop(self):
         """return stop date"""
         raise NotImplementedError
--- a/web/views/editcontroller.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/editcontroller.py	Wed Oct 09 11:13:56 2013 +0200
@@ -28,7 +28,7 @@
 from rql.utils import rqlvar_maker
 
 from cubicweb import Binary, ValidationError
-from cubicweb.view import EntityAdapter, implements_adapter_compat
+from cubicweb.view import EntityAdapter
 from cubicweb.predicates import is_instance
 from cubicweb.web import (INTERNAL_FIELD_VALUE, RequestError, NothingToEdit,
                           ProcessFormError)
@@ -36,7 +36,6 @@
 
 
 class IEditControlAdapter(EntityAdapter):
-    __needs_bw_compat__ = True
     __regid__ = 'IEditControl'
     __select__ = is_instance('Any')
 
@@ -47,7 +46,6 @@
                  DeprecationWarning)
         super(IEditControlAdapter, self).__init__(_cw, **kwargs)
 
-    @implements_adapter_compat('IEditControl')
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
@@ -57,7 +55,6 @@
             return parent.rest_path(), {}
         return str(self.entity.e_schema).lower(), {}
 
-    @implements_adapter_compat('IEditControl')
     def pre_web_edit(self):
         """callback called by the web editcontroller when an entity will be
         created/modified, to let a chance to do some entity specific stuff.
--- a/web/views/ibreadcrumbs.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/ibreadcrumbs.py	Wed Oct 09 11:13:56 2013 +0200
@@ -24,7 +24,6 @@
 
 from logilab.mtconverter import xml_escape
 
-#from cubicweb.interfaces import IBreadCrumbs
 from cubicweb import tags, uilib
 from cubicweb.entity import Entity
 from cubicweb.predicates import (is_instance, one_line_rset, adaptable,
@@ -35,15 +34,6 @@
 # don't use AnyEntity since this may cause bug with isinstance() due to reloading
 
 
-# ease bw compat
-def ibreadcrumb_adapter(entity):
-    if hasattr(entity, 'breadcrumbs'):
-        warn('[3.9] breadcrumbs() method is deprecated, define a custom '
-             'IBreadCrumbsAdapter for %s instead' % entity.__class__,
-             DeprecationWarning)
-        return entity
-    return entity.cw_adapt_to('IBreadCrumbs')
-
 
 class IBreadCrumbsAdapter(EntityAdapter):
     """adapters for entities which can be"located" on some path to display in
@@ -53,11 +43,6 @@
     __select__ = is_instance('Any', accept_none=False)
 
     def parent_entity(self):
-        if hasattr(self.entity, 'parent') and callable(self.entity.parent):
-            warn('[3.9] parent() method is deprecated, define a '
-                 'custom IBreadCrumbsAdapter/ITreeAdapter for %s instead'
-                 % self.entity.__class__, DeprecationWarning)
-            return self.entity.parent()
         itree = self.entity.cw_adapt_to('ITree')
         if itree is not None:
             return itree.parent()
@@ -94,7 +79,7 @@
                 self.error('cycle in breadcrumbs for entity %s' % self.entity)
                 return []
             _recurs.add(parent.eid)
-            adapter = ibreadcrumb_adapter(parent)
+            adapter = parent.cw_adapt_to('IBreadCrumbs')
             path = adapter.breadcrumbs(view, _recurs) + [self.entity]
         else:
             path = [self.entity]
@@ -125,7 +110,7 @@
             entity = self.cw_extra_kwargs['entity']
         except KeyError:
             entity = self.cw_rset.get_entity(0, 0)
-        adapter = ibreadcrumb_adapter(entity)
+        adapter = entity.cw_adapt_to('IBreadCrumbs')
         view = self.cw_extra_kwargs.get('view')
         path = adapter.breadcrumbs(view)
         if path:
--- a/web/views/navigation.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/navigation.py	Wed Oct 09 11:13:56 2013 +0200
@@ -55,10 +55,9 @@
 from logilab.mtconverter import xml_escape
 from logilab.common.deprecation import deprecated
 
-from cubicweb.predicates import (paginated_rset, sorted_rset,
-                                adaptable, implements)
+from cubicweb.predicates import paginated_rset, sorted_rset, adaptable
 from cubicweb.uilib import cut
-from cubicweb.view import EntityAdapter, implements_adapter_compat
+from cubicweb.view import EntityAdapter
 from cubicweb.web.component import EmptyComponent, EntityCtxComponent, NavigationComponent
 
 
@@ -324,7 +323,6 @@
 View.handle_pagination = False
 
 
-from cubicweb.interfaces import IPrevNext
 
 class IPrevNextAdapter(EntityAdapter):
     """Interface for entities which can be linked to a previous and/or next
@@ -335,14 +333,12 @@
     """
     __needs_bw_compat__ = True
     __regid__ = 'IPrevNext'
-    __select__ = implements(IPrevNext, warn=False) # XXX for bw compat, else should be abstract
+    __abstract__ = True
 
-    @implements_adapter_compat('IPrevNext')
     def next_entity(self):
         """return the 'next' entity"""
         raise NotImplementedError
 
-    @implements_adapter_compat('IPrevNext')
     def previous_entity(self):
         """return the 'previous' entity"""
         raise NotImplementedError
--- a/web/views/primary.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/primary.py	Wed Oct 09 11:13:56 2013 +0200
@@ -189,10 +189,6 @@
     def render_entity_toolbox(self, entity):
         self.content_navigation_components('ctxtoolbar')
 
-    @deprecated('[3.8] render_entity_metadata method is deprecated')
-    def render_entity_metadata(self, entity):
-        entity.view('metadata', w=self.w)
-
     def summary(self, entity):
         """default implementation return an empty string"""
         return u''
@@ -217,16 +213,8 @@
         if display_attributes:
             self.w(u'<table>')
             for rschema, role, dispctrl, value in display_attributes:
-                # pylint: disable=E1101
-                if not hasattr(self, '_render_attribute'):
-                    label = self._rel_label(entity, rschema, role, dispctrl)
-                    self.render_attribute(label, value, table=True)
-                else:
-                    warn('[3.9] _render_attribute prototype has changed and '
-                         'renamed to render_attribute, please update %s'
-                         % self.__class__, DeprecationWarning)
-                    self._render_attribute(dispctrl, rschema, value, role=role,
-                                           table=True)
+                label = self._rel_label(entity, rschema, role, dispctrl)
+                self.render_attribute(label, value, table=True)
             self.w(u'</table>')
 
     def render_attribute(self, label, value, table=False):
@@ -253,13 +241,6 @@
                 rset = self._relation_rset(entity, rschema, role, dispctrl, limit=limit)
                 if not rset:
                     continue
-                if hasattr(self, '_render_relation'):
-                    # pylint: disable=E1101
-                    self._render_relation(dispctrl, rset, 'autolimited')
-                    warn('[3.9] _render_relation prototype has changed and has '
-                         'been renamed to render_relation, please update %s'
-                         % self.__class__, DeprecationWarning)
-                    continue
                 try:
                     rview = self._cw.vreg['views'].select(
                         vid, self._cw, rset=rset, dispctrl=dispctrl)
--- a/web/views/reledit.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/reledit.py	Wed Oct 09 11:13:56 2013 +0200
@@ -91,9 +91,6 @@
         rschema = self._cw.vreg.schema[rtype]
         rctrl = self._cw.vreg['uicfg'].select('reledit', self._cw, entity=entity)
         self._rules = rctrl.etype_get(self.entity.e_schema.type, rschema.type, role, '*')
-        if rvid is not None or default_value is not None:
-            warn('[3.9] specifying rvid/default_value on select is deprecated, '
-                 'reledit_ctrl rtag to control this' % self, DeprecationWarning)
         reload = self._compute_reload(rschema, role, reload)
         divid = self._build_divid(rtype, role, self.entity.eid)
         if rschema.final:
@@ -322,19 +319,11 @@
         rdef = entity.e_schema.rdef(rschema)
         return rdef.has_perm(self._cw, 'update', eid=entity.eid)
 
-    should_edit_attributes = deprecated('[3.9] should_edit_attributes is deprecated,'
-                                        ' use _should_edit_attribute instead',
-                                        _should_edit_attribute)
-
     def _should_edit_relation(self, rschema, role):
         eeid = self.entity.eid
         perm_args = {'fromeid': eeid} if role == 'subject' else {'toeid': eeid}
         return rschema.has_perm(self._cw, 'add', **perm_args)
 
-    should_edit_relations = deprecated('[3.9] should_edit_relations is deprecated,'
-                                       ' use _should_edit_relation instead',
-                                       _should_edit_relation)
-
     def _open_form_wrapper(self, divid, value, form, renderer,
                            _edit_related, _add_related, _delete_related):
         w = self.w
--- a/web/views/treeview.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/treeview.py	Wed Oct 09 11:13:56 2013 +0200
@@ -29,13 +29,26 @@
 from cubicweb.utils import make_uid, json
 from cubicweb.predicates import adaptable
 from cubicweb.view import EntityView
-from cubicweb.mixins import _done_init
 from cubicweb.web.views import baseviews
 from cubicweb.web.views.ajaxcontroller import ajaxfunc
 
 def treecookiename(treeid):
     return str('%s-treestate' % treeid)
 
+def _done_init(done, view, row, col):
+    """handle an infinite recursion safety belt"""
+    if done is None:
+        done = set()
+    entity = view.cw_rset.get_entity(row, col)
+    if entity.eid in done:
+        msg = entity._cw._('loop in %(rel)s relation (%(eid)s)') % {
+            'rel': entity.cw_adapt_to('ITree').tree_relation,
+            'eid': entity.eid
+            }
+        return None, msg
+    done.add(entity.eid)
+    return done, entity
+
 
 class BaseTreeView(baseviews.ListView):
     """base tree view"""
--- a/web/views/urlrewrite.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/urlrewrite.py	Wed Oct 09 11:13:56 2013 +0200
@@ -140,7 +140,7 @@
         return None, None
 
 
-def build_rset(rql, rgxgroups=None, cachekey=None, setuser=False,
+def build_rset(rql, rgxgroups=None, setuser=False,
                vid=None, vtitle=None, form={}, **kwargs):
 
     def do_build_rset(inputurl, uri, req, schema, kwargs=kwargs):
@@ -156,7 +156,7 @@
             req.form['vid'] = vid
         if vtitle:
             req.form['vtitle'] = req._(vtitle) % kwargs
-        return None, req.execute(rql, kwargs, cachekey)
+        return None, req.execute(rql, kwargs)
     return do_build_rset
 
 def update_form(**kwargs):
--- a/web/views/xmlrss.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/views/xmlrss.py	Wed Oct 09 11:13:56 2013 +0200
@@ -28,7 +28,6 @@
 from cubicweb.predicates import (is_instance, non_final_entity, one_line_rset,
                                 appobject_selectable, adaptable)
 from cubicweb.view import EntityView, EntityAdapter, AnyRsetView, Component
-from cubicweb.view import implements_adapter_compat
 from cubicweb.uilib import simple_sgml_tag
 from cubicweb.web import httpcache, component
 
@@ -185,7 +184,6 @@
     __regid__ = 'IFeed'
     __select__ = is_instance('Any')
 
-    @implements_adapter_compat('IFeed')
     def rss_feed_url(self):
         """return an url to the rss feed for this entity"""
         return self.entity.absolute_url(vid='rss')
--- a/web/webconfig.py	Mon Sep 30 18:07:51 2013 +0200
+++ b/web/webconfig.py	Wed Oct 09 11:13:56 2013 +0200
@@ -401,7 +401,7 @@
             self._load_ui_properties_file(uiprops, path)
         self._load_ui_properties_file(uiprops, self.apphome)
         datadir_url = uiprops.context['datadir_url']
-        # XXX pre 3.9 css compat
+        # pre 3.9 css compat, however the old css still rules
         if self['use-old-css']:
             if (datadir_url+'/cubicweb.css') in uiprops['STYLESHEETS']:
                 idx = uiprops['STYLESHEETS'].index(datadir_url+'/cubicweb.css')
@@ -413,21 +413,6 @@
             uiprops['JAVASCRIPTS'].insert(0, cubicweb_js_url)
 
     def _load_ui_properties_file(self, uiprops, path):
-        resourcesfile = join(path, 'data', 'external_resources')
-        if exists(resourcesfile):
-            warn('[3.9] %s file is deprecated, use an uiprops.py file'
-                 % resourcesfile, DeprecationWarning)
-            datadir_url = uiprops.context['datadir_url']
-            for rid, val in read_config(resourcesfile).iteritems():
-                if rid in ('STYLESHEETS', 'STYLESHEETS_PRINT',
-                           'IE_STYLESHEETS', 'JAVASCRIPTS'):
-                    val = [w.strip().replace('DATADIR', datadir_url)
-                           for w in val.split(',') if w.strip()]
-                    if rid == 'IE_STYLESHEETS':
-                        rid = 'STYLESHEETS_IE'
-                else:
-                    val = val.strip().replace('DATADIR', datadir_url)
-                uiprops[rid] = val
         uipropsfile = join(path, 'uiprops.py')
         if exists(uipropsfile):
             self.debug('loading %s', uipropsfile)