You are here: Home / What do you need? / Help and documentation / Plone Info / Running Zope/Plone scripts from the command line

Running Zope/Plone scripts from the command line

by Darrell Kingsley last modified Mar 13, 2014 12:53 PM
You need a whole special load of stuff to run Plone or Zope scripts for the command line. These centre around getting hold of the portal object from which everything else is available, setting a fake request object so you can traverse around the site and setting a user so you have permission to do whatever it is you want to do. Then at the end you have to commit your transaction and sync the zeoclients.

Command line scripts get the global variable app from which you can get your portal which you use in place of self and context in scripts e.g.

app.mysite

But there is perhaps a more elegant way to do this, which we can combine with other bits we need to do, so keep reading.

 

Give your script a user

Command line scripts don't have a user, so if you need more than you can do as anonymous, then so this:

from AccessControl.SecurityManagement import newSecurityManager

# Use Zope application server user database (not plone site)
admin=app.acl_users.getUserById("admin")
newSecurityManager(None, admin)

Replace "admin" with the user you need.

 

Create a fake HTTP request

Then you need to spoof the HTTP request object so you can traverse around and grab parts of the site. I had thought this would be irrelevant, but when I tried the script without it, I got skinnable errors, so it is probably required to do pretty much anything to your site. By all means try without it though.

import zope
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManager import setSecurityPolicy
from Testing.makerequest import makerequest
from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy, OmnipotentUser

def spoofRequest(app):
    """
    Make REQUEST variable to be available on the Zope application server.

    This allows acquisition to work properly
    """
    _policy=PermissiveSecurityPolicy()
    _oldpolicy=setSecurityPolicy(_policy)
    newSecurityManager(None, OmnipotentUser().__of__(app.acl_users))
    return makerequest(app)

# Enable Faux HTTP request object
app = spoofRequest(app)

# Get Plone site object from Zope application server root
site = app.unrestrictedTraverse("sitename")
site.setupCurrentSkin(app.REQUEST)
zope.app.component.hooks.setSite(site)

 

Add your code and commit any changes to the ZODB

Once you've done that, you're ready to put your code into the script. Create functions, like you would with an external method and pass site instead of self e.g.

def doMyThang(site):

If you make changes to the ZODB, you'll need to commit them manually as follows:

# Commit transaction
import transaction transaction.commit()
# Perform ZEO client synchronization (if runnning in clustered mode)
app._p_jar.sync()

 

Put it all together

Putting all this together gives the following which can go at the top of your script, including the slightly more elegant way of getting the portal object

import zope
import transaction
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManager import setSecurityPolicy
from Testing.makerequest import makerequest
from Products.CMFCore.tests.base.security import PermissiveSecurityPolicy, OmnipotentUser

# Use Zope application server user database (not plone site)
admin=app.acl_users.getUserById("admin")
newSecurityManager(None, admin)

def spoofRequest(app):
    """
    Make REQUEST variable to be available on the Zope application server.

    This allows acquisition to work properly
    """
    _policy=PermissiveSecurityPolicy()
    _oldpolicy=setSecurityPolicy(_policy)
    newSecurityManager(None, OmnipotentUser().__of__(app.acl_users))
    return makerequest(app)

# Enable Faux HTTP request object
app= spoofRequest(app)

# Get Plone site object from Zope application server root
site = app.unrestrictedTraverse("mysite")
site.setupCurrentSkin(app.REQUEST)
zope.app.component.hooks.setSite(site)

# here's an example function

def doSomeStuff(site):
# go get something and change it, then...
	transaction.commit()
	app._p_jar.sync()

 

Run the script

Now you need the command to actually run the script. If the script is in Extensions, from inside the zeocluster that would be:

sudo ./bin/client2 run ./src/namespace.mypackage/namespace/mypackage/Extensions/script.py

And from cron this might be:

/usr/local/Plone-3.3.5/zeocluster/bin/client2 run /usr/local/Plone-3.3.5/zeocluster/src/namespace.mypackage/namespace/mypackage/Extensions/script.py 1>>/home/plone/sal_updates.log

This info came from the following page, where there also some extra stuff and also how to set up logging as well - http://collective-docs.plone.org/misc/commandline.html.

This no longer exists so try here http://readthedocs.org/docs/collective-docs/en/latest/misc/commandline.html

In the above the

import zope
...
zope.app.component.hooks.setSite(site)

 came from a similar error 

zope.component.interfaces.ComponentLookupError: (<InterfaceClass
Products.CMFCore.interfaces._content.ISiteRoot>, '')

to the one encountered and fixed here

http://plone.293351.n2.nabble.com/plone-scripting-td6341705.html