Upgrade notes

This document outlines how to update Grok applications so that they continue to work with newer versions of the Grok Toolkit. This document only describes changes involving incompatibilities or deprecations, not new features (please refer to CHANGES.txt for those).

Warning. Please be sure to always backup your data (especially the Data.fs file) before you perform upgrades.

Upgrading to 1.5 (2012-05-02)

To upgrade your Grok based project, make sure your project’s buildout.cfg extends from the versions.cfg of the latest Grok Toolkit release. This file can be found here:

http://grok.zope.org/releaseinfo/1.5/versions.cfg

Then re-run bootstrap.py in your project to prevent potential zc.buildout version conflicts. Then run bin/buildout to get the latest versions of all of the packages that comprise the Grok Toolkit.

Upgrading to 1.4 (2011-04-04)

To upgrade your Grok based project, make sure your project’s buildout.cfg extends from the versions.cfg of the latest Grok Toolkit release. This file can be found here:

http://grok.zope.org/releaseinfo/1.4/versions.cfg

Then re-run bootstrap.py in your project to prevent potential zc.buildout version conflicts. Then run bin/buildout to get the latest versions of all of the packages that comprise the Grok Toolkit.

Upgrading to 1.3 (2011-01-20)

To upgrade your Grok based project, make sure your project’s buildout.cfg extends from the versions.cfg of the latest Grok Toolkit release. This file can be found here:

http://grok.zope.org/releaseinfo/1.3/versions.cfg

Then re-run bootstrap.py in your project to prevent potential zc.buildout version conflicts. Then run bin/buildout to get the latest versions of all of the packages that comprise the Grok Toolkit.

Extra notes:

  • For newly created projects using the grokproject tool, z3c.testsetup has been removed in favor of manual registration of tests. Even though tests discovery and registration through z3c.testsetup should still work for existing projects, it is advisable to start registering tests “manually”. More documentation concerning this topic is forthcoming. See also the tests.py module generated by the grokproject tool for examples.

  • The ‘static’ directory is no longer automatically grokked and registered as a DirectoryResource for serving static resources. Serving static resources is being superseded by the Fanstatic library and WSGI components.

    Newly created project using the grokproject tool will have the infrastructure set up for serving static resources, and a compatibility layer has been created through zope.fanstatic. This compatibility layer will allow for computing URLs to resources in Zope Page Templates and in view code.

    The compatibility layer will also set the base_url necessary for the Fanstatic WSGI components to automatically inject resource inclusions in the <head> section of the rendered HMTL, and for serving the resources. Having the Fanstatic WSGI components serve the static resources will off-load this task from Grok, generally improving the application’s performance.

    In order to update your current Grok project to the most recent Grok Toolkit release, you need to register a Fanstatic Library for each of the static directories your project may have. Registering the ‘static’ directory involves three steps:

    1. Add zope.fanstatic and fanstatic to the install_requires in your project’s setup.py. If you are using z3c.autoinclude in your configure.zcml, the necessary component registrations in zope.fanstatic will be done automatically.

      If you do not use z3c.autoinclude, include zope.fanstatic in your configure.zcml.

    2. Include the following code in resource.py:

      from fanstatic import Library
      library = Library(PACKAGENAME, 'static')
      
    3. In the setup.py of your project, add the following entry point (again, for each ‘static’ directory your project may have)

      'fanstatic.libraries': [
        'PACKAGENAME = PACKAGENAME.resource:library'
      ]
      

    Where the PACKAGENAME should be replaced with the dotted package name in which the ‘static’ directory resides.

    The grok.DirectoryResource components have not been removed from Grok. So, the custom directory resources in your project should continue to work unchanged.

  • The way templates are associated to view components has changed. In general, this should not lead to problems, just less spurious warnings when starting your Grok application.

  • The default views that were rendered in case of application errors (like for NotFound exceptions, or Unauthorized exceptions used to be done through the now deprecated zope.app.http and zope.app.exception packages. These packages have been replaced by the zope.errorview package.

    In case your application imports from the aformentiond deprecated package, you should either include these packages in the install_requires section in your project’s setup.py or port the code to use zope.errorview - the later is the recommend practice.

Upgrading to 1.2 (2010-10-26)

To upgrade your Grok based project, make sure your project’s buildout.cfg extends from the versions.cfg of the latest Grok Toolkit release. This file can be found here:

http://grok.zope.org/releaseinfo/1.2/versions.cfg

Then re-run bootstrap.py in your project to prevent potential zc.buildout version conflicts. Then run bin/buildout to get the latest versions of all of the packages that comprise the Grok Toolkit.

Extra notes:

  • In Grok 1.2 the “zdaemon” support has been removed. If your project still relied on this method of starting and stopping the server you will need to update your prokject.

    The easiest way for find what parts and file to add, is to create a new project using the grokproject tool and then to compare its structure to your project’s structure. Look for the “debug_ini” and “deploy_ini” parts in the buildout.cfg file and the corresponding configuration file templates.

  • A large numner of zope.app. dependencies have been removed. Even though most of these packages just contained backwards compatibility imports, your application might still contain imports from these zope.app. packages.

    The best way to update this, is to carefully search for zope.app. imports in your project and to rewrite them to the new import locations. Note though, that in rare cases, this might break persisted object in your database.

    If this is the case, you will need to add the particular zope.app. package to the install_requires section in the setup.py of your project. After a re-run of ./bin/buildout these package are now again available to your project.

    In general it is good practice to explicitely list the dependencies of your project in the setup.py. In other words: if your project imports from packages, these packages should be listed in the install_requires of your project’s setup.py.

  • One specific case of a zope.app. depdendency that got removed and replaced is zope.app.testing that is used in the test setup of your project. Most of the functionality in zope.app.testing has been rebuild in zope.component.testlayer, zope.app.appsetup, and zope.app.wsgi.testlayer.

    Again, the easiest way to upgrade this part of your project, is to look a freshly created project using the grokproject tool. There, the test setup code uses the “new style” test layers for functional testing.

  • In some case the configuration step of a dependency is not triggered anymore, where it used to be before most of the zope.app. dependencies got removed. If this is the case (for example for components in in zope.pluggableauth.plugins), you will need to trigger the configuration yourself from your project’s configure.zcml like so:

    <include package="[PACKAGE_TO_CONFIGURE]" />
    

    or, for a specific *.zcml file in package:

    <include package="[PACKAGE_TO_CONFIGURE]" file="[ZCML_FILE]"/>
    

Upgrading to 1.1 (2010-05-18)

  • z3c.testsetup dependency has been removed from grok. If you use grok.testing.register_all_tests in your testsetup, make sure to require z3c.testsetup (and also zope.app.testing, if you use functional tests) in setup.py of your project and use:

    import z3c.testsetsetup
    z3c.testsetup.register_all_tests(...)
    

    where you used grok.testing.register_all_tests(...) before.

Upgrading to 1.1rc1 (2010-02-25)

  • When upgrading to a newer version of Grok, you should refer to the newest list of versions as defined for this release of Grok.

    For Grok 1.1rc1, download the following file:

    http://grok.zope.org/releaseinfo/grok-1.1rc1.cfg
    

    And replace versions.cfg in your project with this file. Then run:

    ./bin/buildout
    

Upgrading to 1.1a2 (2009-12-22)

  • When upgrading to a newer version of Grok, you should refer to the newest list of versions as defined for this release of Grok.

    For Grok 1.1a2, download the following file:

    http://grok.zope.org/releaseinfo/grok-1.1a2.cfg
    

    And replace versions.cfg in your project with this file. Then run:

    ./bin/buildout
    

Upgrading to 1.1a1 (2009-11-17)

  • When upgrading to a newer version of Grok, you should refer to the newest list of versions as defined for this release of Grok.

    For Grok 1.1a1, download the following file:

    http://grok.zope.org/releaseinfo/grok-1.1a1.cfg
    

    And replace versions.cfg in your project with this file. Then run:

    ./bin/buildout
    

Upgrading to 1.0b2 (2009-09-15)

  • The default permission is now zope.View as a replacement for zope.Public. You need to add this permission as a default permission to all users (use zope.Anybody). This requires a change to site.zcml as follows:

    <grant permission="zope.View"
           principal="zope.Anybody" />
    

    If you do not make this change, all of your views will be unavailable for anonymous and the system will ask the user for a password.

    This change allows you to protect Grok views that come with the default permission by modifying your site.zcml.

    If you used a previous version of Grok or grokproject you may still have a reference to grok.View in site.zcml or ftesting.zcml. This will lead to an error, as Grok does not define grok.View anymore. It’s safe to remove such references.

  • When upgrading to a newer version of Grok, you should refer to the newest list of versions as defined for this release of Grok.

    For Grok 1.0b2, download the following file:

    http://grok.zope.org/releaseinfo/grok-1.0b2.cfg
    

    And replace versions.cfg in your project with this file.

  • There have been a lot of changes in grokproject and the project layout it generates. This gives you a short description of how you can upgrade your existing Grok project that was generated with an older version of grokproject.

    You should upgrade grokproject as follows:

    $ easy_install -U grokproject
    

    grokproject now creates a project based on Paste Deploy, along with a few other changes to the way various files are managed.

    Warning. Please be aware that the parts/data and parts/log directories are deprecated and will not be used anymore. The only reasons there are still [data] and [log] sections in the new buildout.cfg``structure is to make sure we don't accidentally throw away important data. Back up at least any important ``Data.fs (which contains the ZODB object database of your app) before making any changes to a project, or when you upgrade any deployment.

    To switch over your project to use a new layout, create a new, empty project with grokproject and compare it with your current layout. You can then compare it with your existing project layout and make the appropriate changes to your project.

    There have been changes to setup.py, buildout.cfg and a new etc directory has appeared.

    The templates for config files are now in etc - these get generated by buildout into parts/etc. Find more information on configuration and settings in etc/README.txt.

    The Data.fs file is now placed in var/filestorage. Please copy your backed-up version of Data.fs in there to reuse your older ZODB database.

    Log files are now placed in var/log.

    Before re-running buildout to generate the new templates, please be aware that parts/zopectl and parts/app will be deleted when you re-run buildout. This means that parts/zopectl/access.log will be removed, and you may want to backup this file first.

    After making these adjustments, you can now run buildout for the project.

    Start the instance now like:

    bin/paster serve parts/etc/deploy.ini
    

    or with:

    bin/projectname-ctl fg
    

    Alternatively there’s a profile available that can help debugging errors in your application:

    bin/paster serve parts/etc/debug.ini
    

    When using this profile it is not the zope.publisher that handles the exceptions that are raised, but a special middleware is. This middleware then provides a pdb-like debugging user interface in the browser.

    Note that this includes IUnauthorized exceptions not being handled by zope, that would’ve prevented any login mechanism to work when debugging.

    However, there is a configuration option called exempt-exceptions available in the debug.ini that determines what exceptions should still be handled by zope. By default debug.ini files created by grokproject will exempt the IUnauthorized exceptions from being reraised and thus normal authentication mechanism continue to work:

    [app:zope]
    use = egg:${egg}#debug
    filter-with = translogger
    exempt-exceptions = zope.security.interfaces.IUnauthorized
    

    Interpreter name has been changed from bin/python to bin/grokpy to avoid conflicts with virtualenv.

  • Old buildout.cfg contain a find-links line like this:

    find-links = http://download.zope.org/distribution/
    

    You should be able to safely remove this, as this points to a repository of old releases that Grok doesn’t depend on any more.

    If you have problems after removing it and re-running bin/buildout, you can add it again however. We know that megrok.form for instance depends on a release that was only available in this repository.

  • Note: in 1.0b1 we used to have a split between View and CodeView. This split got reverted. Grok’s behavior is still the same as in the 1.0a versions - views and the render method continue to work as they did before.

Upgrading to 1.0b1 (2009-09-14)

  • This release happened was never completed. See the upgrade documentation for 1.0b2 instead.

Upgrading to 1.0a1

  • The grok.RESTProtocol class has been removed in favour of a grok.restskin() directive for interfaces. For instance, if you previously registered a REST layer as a skin like so:

    class IMyLayer(grok.IRESTLayer):
        pass
    
    class MyRestProtocol(grok.RESTProtocol):
        grok.layer(IMyLayer)
    

    You can now simply write:

    class IMyLayer(grok.IRESTLayer):
        grok.restskin('myskin')
    

    As you can see, IRESTLayer has been introduced as a baseclass for defining REST layers.

Upgrading to 0.14

  • The grok.Skin class has been removed in favour of a grok.skin() directive for interfaces. For instance, if you previously registered a browser layer as a skin like so:

    class IMyLayer(grok.IGrokLayer):
        pass
    
    class MySkin(grok.Skin):
        grok.layer(IMyLayer)
    

    You can now simply write:

    class IMyLayer(grok.IBrowserRequest):
        grok.skin('myskin')
    

    As you can see, IGrokLayer has also been removed, in favour of the exposure of IBrowserRequest.

  • The grok.admin subpackage has been factored out to a separate package grokui.admin. To have the Grok admin UI available in your environment, add grokui.admin to the required packages in the setup.py of your package.

    A new Grok application will have an install_requires parameter that looks like this:

    install_requires=['setuptools',
                      'grok',
                      'grokui.admin',
                      'z3c.testsetup',
                      # Add extra requirements here
                      ],
    

Upgrading to 0.13

  • The directive implementations changed tremendously with the upgrade to Martian 0.10. Custom implementations of both directives (see next bullet point) and grokkers will have to be adjusted.

    Since the vast majority of directives are class directives, the most common places where information set by directives has to be read are class grokkers (martian.ClassGrokker). For instance, you may have written something like this to implement a custom class grokker previously:

    class RobotGrokker(martian.ClassGrokker):
        component_class = Robot
    
        def grok(self, name, factory, module_info, config, **kw):
            robot_name = martian.util.class_annotation(factory, 'grok.name', '')
            title = martian.util.class_annotation(factory, 'grok.title', 'A robot')
            provides = martian.util.class_annotation(factory, 'grok.provides', None)
            if provides is None:
                martian.util.check_implements_one(factory)
                provides = list(zope.interface.implementedBy(factory))[0]
            config.action(
                descriminator=('robot', provides, robot_name),
                callable=provideRobot,
                args=(factory, provides, robot_name, title),
                )
            return True
    

    As you can see, this grokker needs to retrieve three values from the class it’s grokking (factory) which are all set by directives:

    • grok.name with the standard default, an empty string,

    • grok.title with a custom default, the string A robot,

    • grok.provides with a computed default.

    With the new directive implementation and the extensions to Martian’s ClassGrokker, you are now able to write (and you should write!):

    def default_provides(factory, module, **data):
        # This function is available for import from grokcore.component.meta.
        # It's shown here simply to illustrate how the original grokker would
        # have been refactored.
        martian.util.check_implements_one(factory)
        return list(zope.interface.implementedBy(factory))[0]
    
    class RobotGrokker(martian.ClassGrokker):
        martian.component(Robot)
        martian.directive(grok.name, name='robot_name')
        martian.directive(grok.title, default='A Robot')
        martian.directive(grok.provides, get_default=default_provides)
    
        def execute(self, factory, config, robot_name, title, provides, **kw):
            config.action(
                descriminator=('robot', provides, robot_name),
                callable=provideRobot,
                args=(factory, provides, robot_name, title),
                )
            return True
    

    What you need to do is provide the directives in the grokker class using martian.directive and then implement the execute method which will get the class (factory) and the configuration context (config) as positional arguments and then the values of the directives as keyword parameters.

    Note that when using martian.directive, you may

    • set the name of the keyword parameter if you want it to be different than the directive’s name,

    • set a default value if you want it to be different from the directive’s standard default,

    • pass in a factory for a computed default value (get_default).

    If you need still need to manually retrieve directive values from an object (a class, an instance or a module), you can do so by explicitly calling bind on the directive (which accepts the same optional parameters as martian.directive), and then the get method of the bound directive, e.g.:

    class_context = grok.context.bind().get(factory, module=module)
    just_module_context = grok.context.bind().get(module=module)
    

    In most cases it’s possible to avoid this though, and use the martian.directive directive on the class level.

    You can look at src/grok/meta.py in Grok to see examples.

  • Your custom grokker could previously use component_class and priority as class-level variables. These have been changed to the martian.component and the martian.priority directives that take the value as its first argument. The new martian.directive directive was introduced above.

  • Custom directives need to be re-implemented using Martian’s new Directive base class. The directive scope, the type of storage, the validator and a potential default value are all defined as class-level variables:

    • The directive scope can either one of martian.CLASS, martian.MODULE, martian.CLASS_OR_MODULE.

    • The type of storage can be either one of martian.ONCE, martian.MULTIPLE, martian.DICT.

    • An optional validator may be one of validateText, validateInterface, validateInterfaceOrClass or a custom method.

    • Unless set with a different value, the standard default value will be None.

    For example, consider the implementation of the grok.name directive:

    class name(martian.Directive):
        scope = martian.CLASS
        store = martian.ONCE
        default = u''
        validate = martian.validateText
    

    Or a bit more involved (and made-up) example:

    class bases(martian.Directive):
        scope = martian.CLASS
        scope = martian.ONCE
        default = []
    
        # The factory is called with the parameters of the directive
        # and may transform the values into whatever should be stored.
        def factory(self, *values):
            return list(values)
    
        # This validator makes sure that the directive can only take
        # a list of classes an argument
        def validate(self, *values):
            for value in values:
                if not isinstance(value, type):
                    raise GrokError("%r is not a class!" % value)
    
  • We moved to newer versions of zope packages. Grok’s versions for Zope packages are now based on the KGS list for Zope 3.4c1 (the latest list). This means your code can now get some new deprecation warnings for imports that have been moved. Please check your code and fix your imports if you get those warnings.

  • If you were using zope.publisher.http.applySkin, you now must use grok.util.applySkin. This because zope.publisher.http.appySkin was removed again in later versions of zope.publisher.

  • The url method on ViewletManager and Viewlet was removed. Instead you can easily access the url method of the view itself from within a viewlet or viewlet manager, and the view name is also available in viewlet templates. There are also new viewlet and viewletmanager namespaces in the viewlet templates. Note that view in a viewlet thus means something else than what it does before. Previous uses of view in a viewlet template should be renamed to viewlet.

Upgrading to 0.12

  • Please upgrade grokproject:

    $ easy_install -U grokproject
    
  • If you have existing Grok projects and you want to make use of Grok’s new autoinclusion functionality in them, you can place the following line in your project’s configure.zcml:

    <includeDependencies package=”.” />

    This will cause the ZCML for setup.py dependencies of your package to be loaded automatically. You can now get rid of any manual include statements (except the one that includes grok itself).

    For new projects created by grokproject, this line will be automatically be added for you and you don’t have to do anything except to upgrade grokproject:

    $ easy_install -U grokproject
    
  • The convention that classes ending with -Base automatically become base classes has been removed with martian 0.9.4. Please add the grok.baseclass() directive to these classes explicitly where the ‘Base’ class convention was relied upon to preserve existing functionality.

Upgrading to 0.11

  • grok.define_permission has been removed in favour of a grok.Permission base class, for reasons of symmetry. Instead of writing:

    grok.define_permission('myapp.ViewCavePainting')
    

    you should now write:

    class View(grok.Permission):
        grok.name('myapp.ViewCavePainting')
    

    If you also want to supply a title and description for the permission, use the grok.title() and grok.description() directives on the class.

  • grok.grok and grok.grok_component have been deprecated. If you need them for tests (which is their only legimitate use), you should import them both from grok.testing.

  • Grokkers should now emit configuration actions instead of registering components right away. For that they now get a new keyword argument called config, the configuration context. For example, a grokker that used to do this:

    registerSomeComponent(foo, name)
    

    should now be doing this:

    config.action(
        discriminator=('somecomponent', name),
        callable=registerSomeComponent,
        args=(name,)
        )
    

    The discriminator should be chosen so that registrations with the same discriminator conflict (in the above example, if somebody tried to register two different components under the same name, you’d get a conflict).

  • Grokkers no longer get the context and templates keyword arguments. If they need access to these values, they can now get them as module annotations from the module_info object like this:

    context = module_info.getAnnotation('grok.context')
    templates = module_info.getAnnotation('grok.templates')
    
  • Note that grokkers must always take arbitrary keyword arguments (**kw), as specified by the martian.interfaces.IGrokker interface. A minimal specification of the grok() method is therefore:

    def grok(self, name, obj, **kw):
        ...
    

    though grokkers will likely want to take module_info as well as config explicitly:

    def grok(self, name, obj, module_info, config, **kw):
        ...
    

    If your application defines custom grokkers and you’re getting a TypeError about unexpected arguments to grok, you likely need to update the signature of the grok() method like described above.

Upgrading to 0.10

There were no incompatible changes.