[Checkins] SVN: z3c.recipe.usercrontab/trunk/ import initial revision

Jasper Spaans jspaans at thehealthagency.com
Mon Feb 23 08:28:12 EST 2009


Log message for revision 97160:
  import initial revision
  
  

Changed:
  A   z3c.recipe.usercrontab/trunk/CHANGES.txt
  A   z3c.recipe.usercrontab/trunk/README.txt
  A   z3c.recipe.usercrontab/trunk/buildout.cfg
  A   z3c.recipe.usercrontab/trunk/setup.py
  A   z3c.recipe.usercrontab/trunk/src/
  A   z3c.recipe.usercrontab/trunk/src/z3c/
  A   z3c.recipe.usercrontab/trunk/src/z3c/__init__.py
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/__init__.py
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/README.txt
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/__init__.py
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/rtests.py
  A   z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/usercrontab.py

-=-
Added: z3c.recipe.usercrontab/trunk/CHANGES.txt
===================================================================
--- z3c.recipe.usercrontab/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/CHANGES.txt	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,21 @@
+z3c.recipe.usercrontab changes
+******************************
+
+0.3 (unreleased)
+================
+
+* Renamed to z3c.recipe.usercrontab
+* Add an option to change the command used to read and write crontabs
+* Improved tests to not modify the real crontab
+
+0.2 (2008-01-12)
+================
+
+* Warn if an entry cannot be removed in buildout uninstall
+* Break if multiple entries would be removed in buildout uninstall
+* Have del_entry return the number of removed
+
+0.1 (2008-01-12)
+================
+
+* Initial release.


Property changes on: z3c.recipe.usercrontab/trunk/CHANGES.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/README.txt
===================================================================
--- z3c.recipe.usercrontab/trunk/README.txt	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/README.txt	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,35 @@
+======================
+z3c.recipe.usercrontab
+======================
+
+The problem
+===========
+
+When deploying applications, it can be useful to have maintenance
+tasks be started periodically. On Unix platforms this is usually done
+using ``cron`` which starts `cronjobs`. Adding cronjobs to the
+system-wide cron directory (for example by placing a file in
+``/etc/cron.d``) can be handled using the ``zc.recipe.deployment``
+package, but it does not support adding cronjobs by normal
+users. (as ``/etc/cron.d`` usually is world-writable).
+
+The solution
+============
+``z3c.recipe.usercrontab`` interfaces with cron using ``crontab(1)``,
+and allows normal users to install their own cronjobs. This is done by
+having buildout add and remove cronjobs when installing and
+uninstalling packages.
+
+How to use it
+=============
+
+To use ``z3c.recipe.usercrontab`` you need to add the following to
+your buildout.cfg::
+
+ [mycronjob]
+ recipe = z3c.recipe.usercrontab
+ times = 0 12 * * *
+ command = echo nothing happens at noon
+
+and finally add ``mycronjob`` to the ``parts`` line(s) of your
+buildout.cfg


Property changes on: z3c.recipe.usercrontab/trunk/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/buildout.cfg
===================================================================
--- z3c.recipe.usercrontab/trunk/buildout.cfg	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/buildout.cfg	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,15 @@
+[buildout]
+develop = .
+parts = test
+prefer-final = true
+unzip = true
+versions = versions
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.recipe.usercrontab
+defaults = '--tests-pattern rtests'.split()
+
+[versions]
+zc.recipe.egg >= 1.1.0
+zope.testing = 3.6.0

Added: z3c.recipe.usercrontab/trunk/setup.py
===================================================================
--- z3c.recipe.usercrontab/trunk/setup.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/setup.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,47 @@
+# Copyright (c) 2009 Zope Foundation and contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+
+import os
+from setuptools import setup, find_packages
+
+version = '0.3dev'
+name = 'z3c.recipe.usercrontab'
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(name=name,
+      version=version,
+      author='Jasper Spaans, Jan-Jaap Driessen',
+      author_email='jspaans at thehealthagency.com',
+      license='ZPL',
+      classifiers=[
+          "Development Status :: 4 - Beta",
+          "Framework :: Buildout",
+          "Intended Audience :: Developers",
+          "Topic :: Software Development :: Build Tools",
+          "Topic :: Software Development :: Libraries :: Python Modules",
+          "License :: OSI Approved :: Zope Public License"
+          ],
+      description="User Crontab install buildout recipe",
+      long_description=(read('README.txt') + '\n' +
+                        'Detailed documentation\n' +
+                        '======================\n' +
+                        read('src/z3c/recipe/usercrontab/README.txt')),
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      namespace_packages=['z3c', 'z3c.recipe'],
+      include_package_data=True,
+      install_requires=['setuptools', 'zc.buildout'],
+      entry_points={
+          'zc.buildout': ['default = %s:UserCrontab' % name],
+          'zc.buildout.uninstall': ['default = %s:uninstall_usercrontab' % name]
+      }
+      )


Property changes on: z3c.recipe.usercrontab/trunk/setup.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/__init__.py
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/__init__.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/__init__.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/recipe/__init__.py
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/recipe/__init__.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/recipe/__init__.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/recipe/__init__.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/README.txt
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/README.txt	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/README.txt	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,242 @@
+# Copyright (c) 2009 Zope Foundation and contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+
+The recipe z3c.recipe.usercrontab is a small recipe to facilitate the
+installing of cronjobs into user crontabs.
+
+  >>> from z3c.recipe.usercrontab.usercrontab import UserCrontabManager
+  >>> c = UserCrontabManager()
+
+For these tests, we fake a crontab by filling the list of cron entries
+for this object::
+
+  >>> c.crontab = [ 'MAILTO=""', '@reboot echo "No-one will see this"']
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+
+Now, we're adding a method to it using the official way::
+
+  >>> c.add_entry('@reboot echo "example.com gets spammed!"',
+  ...             MAILTO="example at example.com")
+
+The object also has a convenient __repr__, so we can test its output::
+
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+  MAILTO=example at example.com
+  @reboot echo "example.com gets spammed!"
+
+Adding another entry with yet another MAILTO line is placed at the end::
+
+  >>> c.add_entry('@reboot echo "example.com gets spammed twice!"',
+  ...              MAILTO="twice at example.com")
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+  MAILTO=example at example.com
+  @reboot echo "example.com gets spammed!"
+  MAILTO=twice at example.com
+  @reboot echo "example.com gets spammed twice!"
+
+When another entry is made with the same MAILTO, the MAILTO clause is
+not repeated again::
+
+  >>> c.add_entry('@reboot echo "twice at example.com gets spammed twice!"',
+  ...             MAILTO="twice at example.com")
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+  MAILTO=example at example.com
+  @reboot echo "example.com gets spammed!"
+  MAILTO=twice at example.com
+  @reboot echo "twice at example.com gets spammed twice!"
+  @reboot echo "example.com gets spammed twice!"
+
+Removing entries also works, and removes superfluous environment variables::
+
+  >>> c.del_entry('@reboot echo "example.com gets spammed!"') == 1
+  True
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+  MAILTO=twice at example.com
+  @reboot echo "twice at example.com gets spammed twice!"
+  @reboot echo "example.com gets spammed twice!"
+
+Removing entries does not remove too much::
+
+  >>> c.del_entry('@reboot echo "twice at example.com gets spammed twice!"') == 1
+  True
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+  MAILTO=twice at example.com
+  @reboot echo "example.com gets spammed twice!"
+
+Removing the last entry also removes the dangling MAILTO line::
+
+  >>> c.del_entry('@reboot echo "example.com gets spammed twice!"') == 1
+  True
+  >>> print c
+  MAILTO=""
+  @reboot echo "No-one will see this"
+
+Removing the final entry removes the remaining MAILTO line, leaving us
+with an empty list::
+
+  >>> c.del_entry('@reboot echo "No-one will see this"') == 1
+  True
+  >>> len(c.crontab)
+  0
+
+Adding an entry without a MAILTO environment line also doesn't put in
+an empty one::
+
+  >>> c.add_entry('@reboot echo "Someone will see this"')
+  >>> print c
+  @reboot echo "Someone will see this"
+
+Adding an entry with an empty MAILTO line adds it at the end, so the
+first entry is not disturbed::
+
+  >>> c.add_entry('@reboot echo "No-one will see this"', MAILTO="")
+  >>> print c
+  @reboot echo "Someone will see this"
+  MAILTO=""
+  @reboot echo "No-one will see this"
+
+Next, test the read_crontab and write_crontab methods; we'll use
+``cat`` and a temporary file to not modifiy the crontab of the user
+running these tests::
+
+  >>> import tempfile
+  >>> t = tempfile.NamedTemporaryFile('w')
+  >>> crontestfile = t.name
+  >>> t.write("#dummy\n")
+
+  >>> c = UserCrontabManager(readcrontab="cat %s" % crontestfile,
+  ...                        writecrontab="cat >%s" % crontestfile)
+  >>> c.read_crontab()
+  >>> a = repr(c)
+  >>> c.add_entry('# improbable entry')
+  >>> c.write_crontab()
+  >>> c.read_crontab()
+  >>> b =repr(c)
+  >>> a == b
+  False
+
+Now, delete this entry again and make sure the old crontab is restored::
+
+  >>> c.del_entry('# improbable entry') == 1
+  True
+  >>> c.write_crontab()
+  >>> c.read_crontab()
+  >>> b = repr(c)
+  >>> a == b
+  True
+
+Do the buildout shuffle::
+
+  >>> write('buildout.cfg',
+  ... '''
+  ... [buildout]
+  ... parts = foo
+  ...
+  ... [foo]
+  ... recipe = z3c.recipe.usercrontab
+  ... times = # @reboot
+  ... command = echo nothing happens
+  ... readcrontab = cat %(crontest)s
+  ... writecrontab = cat >%(crontest)s
+  ...
+  ... [bar]
+  ... recipe = z3c.recipe.usercrontab
+  ... times = # @reboot
+  ... command = echo nothing happens
+  ... readcrontab = cat %(crontest)s
+  ... writecrontab = cat >%(crontest)s
+  ... ''' % ( { 'crontest': crontestfile } ))
+
+
+  >>> import os
+  >>> print system(os.path.join('bin', 'buildout'))
+  Installing foo.
+  <BLANKLINE>
+
+Check that it really was added to the crontab::
+
+  >>> c.read_crontab()
+  >>> b = repr(c)
+  >>> a == b
+  False
+
+  >>> '# @reboot\techo nothing happens' in c.crontab
+  True
+
+  >>> 'WARNING=The entries below were generated by buildout, do not modify' in c.crontab
+  True
+
+Uninstall the recipe::
+
+  >>> print system(os.path.join('bin', 'buildout')+' buildout:parts=')
+  Uninstalling foo.
+  Running uninstall recipe.
+  <BLANKLINE>
+
+And check that its entry was removed (i.e., the contents of the
+crontab are the same as when this test was started; in any case, the
+teardown from the testrunner makes sure the old situation is
+restored)::
+
+  >>> c.read_crontab()
+  >>> b = repr(c)
+  >>> a == b
+  True
+
+Now, break it by adding the same crontab entry twice::
+
+  >>> print system(os.path.join('bin', 'buildout')+' "buildout:parts=foo bar"')
+  Installing foo.
+  Installing bar.
+  <BLANKLINE>
+
+  >>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
+  Uninstalling bar.
+  Running uninstall recipe.
+  bar: FATAL ERROR: Found more than one matching crontab-entry during uninstall; please resolve manually.
+  Matched lines: # @reboot echo nothing happens
+  While:
+    Installing.
+    Uninstalling bar.
+  <BLANKLINE>
+  An internal error occured due to a bug in either zc.buildout or in a
+  recipe being used:
+  Traceback (most recent call last):
+  ...
+  RuntimeError: Found more than one matching crontab-entry during uninstall
+  <BLANKLINE>
+
+Manually fix it by removing the offending lines::
+  >>> c.read_crontab()
+  >>> c.del_entry("# @reboot\techo nothing happens")
+  2
+  >>> c.write_crontab()
+
+And now we can uninstall again (albeit with some warnings)::
+  >>> print system(os.path.join('bin', 'buildout')+' buildout:parts=') # doctest:
+  Uninstalling bar.
+  Running uninstall recipe.
+  bar: WARNING: Did not find a crontab-entry during uninstall; please check manually if everything was removed correctly
+  Uninstalling foo.
+  Running uninstall recipe.
+  foo: WARNING: Did not find a crontab-entry during uninstall; please check manually if everything was removed correctly
+  <BLANKLINE>


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/__init__.py
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/__init__.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/__init__.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,52 @@
+# Copyright (c) 2009 Zope Foundation and contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+
+import logging
+from z3c.recipe.usercrontab.usercrontab import UserCrontabManager
+
+class UserCrontab:
+    def __init__(self, buildout, name, options):
+        self.options = options
+
+        options['entry'] = '%s\t%s' % (options['times'], options['command'])
+        readcrontab = self.options.get('readcrontab', None)
+        writecrontab = self.options.get('writecrontab', None)
+
+        self.crontab = UserCrontabManager(readcrontab, writecrontab)
+        self.env = {  'WARNING' :
+              'The entries below were generated by buildout, do not modify' }
+
+    def install(self):
+        crontab = self.crontab
+
+        crontab.read_crontab()
+        crontab.add_entry(self.options['entry'], **self.env)
+        crontab.write_crontab()
+
+        return ()
+
+    def update(self):
+        pass
+
+def uninstall_usercrontab(name, options):
+    readcrontab = options.get('readcrontab', None)
+    writecrontab = options.get('writecrontab', None)
+
+    crontab = UserCrontabManager(readcrontab, writecrontab)
+    crontab.read_crontab()
+    nuked = crontab.del_entry(options['entry'])
+    if nuked==0:
+        logging.getLogger(name).warning("WARNING: Did not find a crontab-entry during uninstall; "
+                                        "please check manually if everything was removed correctly")
+    elif nuked > 1:
+        logging.getLogger(name).error("FATAL ERROR: Found more than one matching crontab-entry during uninstall; "
+                                      "please resolve manually.\nMatched lines: %s" % (options['entry']))
+        raise RuntimeError("Found more than one matching crontab-entry during uninstall")
+    crontab.write_crontab()


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/__init__.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/rtests.py
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/rtests.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/rtests.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,32 @@
+# Copyright (c) 2007-2009 Zope Foundation and contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+
+import zc.buildout.testing
+
+import unittest
+import zope.testing
+from zope.testing import doctest, renormalizing
+
+from z3c.recipe.usercrontab import UserCrontabManager
+
+usercrontab = UserCrontabManager()
+
+def setUp(test):
+    zc.buildout.testing.buildoutSetUp(test)
+    usercrontab.read_crontab()
+    zc.buildout.testing.install_develop('z3c.recipe.usercrontab', test)
+
+def tearDown(test):
+    zc.buildout.testing.buildoutTearDown(test)
+    usercrontab.write_crontab()
+
+def test_suite():
+    return unittest.TestSuite(doctest.DocFileSuite('README.txt', setUp=setUp,
+                                                   tearDown=tearDown))


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/rtests.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/usercrontab.py
===================================================================
--- z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/usercrontab.py	                        (rev 0)
+++ z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/usercrontab.py	2009-02-23 13:28:11 UTC (rev 97160)
@@ -0,0 +1,146 @@
+# Copyright (c) 2009 Zope Foundation and contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+
+import os
+import re
+
+def escape_string(s):
+    """
+    Do a smart escape of string s, wrapping it in quotes and escaping
+    those quotes if necessary. If the first or last byte is a space,
+    also quote the string.
+    """
+    for c,d in (('"', "'"), ("'", '"')):
+        if c in s:
+            return '%s%s%s' % (d, s.replace(d, '\\'+d), d)
+    if len(s)==0 or s[0].isspace() or s[-1].isspace():
+        return '"%s"' % s
+
+    return s
+
+def unescape_string(s):
+    """
+    Unescape any escaped quotes, and remove quotes around the string
+    """
+    if len(s)==0:
+        return s
+    for c in ('"', "'"):
+        if(s[0]==c and s[-1]==c):
+            return s[1:-1].replace('\\'+c, c)
+    return s
+
+
+def dict_pmatch(d1, d2):
+    """
+    Returns true if all keys in d1 are in d2 and all values match
+    """
+    for k,v in d1.iteritems():
+        if not (k in d2 and d2[k]==v):
+            return False
+    return True
+
+
+env_re = re.compile(r'^("[^"]*"|\'[^\']*\'|[^\s]+)\s*='
+                     '\s*("[^"]*"|\'[^\']*\'|[^"\']+)?[^\s]*$')
+
+defaultreadcrontab = "crontab -l"
+defaultwritecrontab = "crontab -"
+
+class UserCrontabManager(object):
+    """
+    Helper class to edit entries in user crontabs (see man 5 crontab)
+    """
+
+    username = None
+    crontab = []
+
+    def __init__(self, readcrontab=defaultreadcrontab,
+                       writecrontab=defaultwritecrontab):
+        self.readcrontab = readcrontab
+        self.writecrontab = writecrontab
+
+    def read_crontab(self):
+        self.crontab = [ l.strip("\n") for l in
+                         os.popen(self.readcrontab, "r") ]
+
+    def write_crontab(self):
+        fd = os.popen(self.writecrontab, "w")
+        for l in self.crontab:
+            fd.write("%s\n" % l)
+        fd.close()
+
+    def __repr__(self):
+        return "\n".join(self.crontab)
+
+    def add_entry(self, line, **env):
+        """
+        Add an entry to a crontab, if kw's are set, set environment args
+        to be like that.
+        """
+        cur_env = {}
+        new_crontab = []
+        done = False
+
+        for l in self.crontab:
+            m = env_re.match(l)
+            if m:
+                cur_env[unescape_string(m.group(1))] = unescape_string(m.group(2))
+            new_crontab.append(l)
+            if not done and dict_pmatch(env, cur_env):
+                new_crontab.append(line)
+                done = True
+
+        if (not done):
+            for (k,v) in env.iteritems():
+                if k not in cur_env or cur_env[k] != v:
+                    new_crontab.append('%s=%s' % (escape_string(k),
+                                                  escape_string(v)))
+            new_crontab.append(line)
+
+        self.crontab = new_crontab
+
+
+    def del_entry(self, line):
+        """
+        Remove an entry from a crontab, dropping useless environment
+        args at the end of the crontab, and which are replaced by
+        something else before an actual crontab entry is found.
+
+        (If the same entry occurs multiple times, it is removed several
+         times)
+        """
+        new_crontab = []
+        fresh_env = {}
+        nuked = True
+        dangling = True
+        num_nuked = 0
+        for l in reversed(self.crontab):
+            m = env_re.match(l)
+            if m:
+                k,v = unescape_string(m.group(1)), unescape_string(m.group(2))
+                if dangling:
+                    continue
+                if nuked is True:
+                    if k in fresh_env:
+                        continue
+                fresh_env[k] = v
+            else:
+                if l==line:
+                    nuked=True
+                    num_nuked = num_nuked + 1
+                    continue
+                else:
+                    if len(l.strip()):
+                       dangling = False
+                       nuked=False
+                       fresh_env = {}
+            new_crontab.append(l)
+        self.crontab = [l for l in reversed(new_crontab) ]
+        return num_nuked


Property changes on: z3c.recipe.usercrontab/trunk/src/z3c/recipe/usercrontab/usercrontab.py
___________________________________________________________________
Added: svn:eol-style
   + native



More information about the Checkins mailing list