aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Bachelier <laurent@bachelier.name>2010-07-05 23:29:21 +0200
committerLaurent Bachelier <laurent@bachelier.name>2010-07-05 23:29:21 +0200
commitcd09fb85ac29aaa77385b9ed7dd1a7c2b91cb101 (patch)
treeb87ccec91b854af62bd6ecc5a70d53ce0bf2605e
parentSimpler code (diff)
downloadconfman-cd09fb85ac29aaa77385b9ed7dd1a7c2b91cb101.tar.xz
Add support for templates in programmable actions
Based on standard Python templates. Some small code fixes too.
-rw-r--r--confman.py83
-rw-r--r--samples/_template22
-rw-r--r--samples/copyfile.copy1
-rw-r--r--samples/template1.p.py4
-rw-r--r--samples/template2.p.py1
5 files changed, 85 insertions, 6 deletions
diff --git a/confman.py b/confman.py
index 304d3d4..4c8e9e4 100644
--- a/confman.py
+++ b/confman.py
@@ -3,6 +3,7 @@ from __future__ import with_statement
import os
import re
import os.path as osp
+from string import Template
# Python <2.6 compatibility
try:
@@ -47,10 +48,10 @@ class Action(object):
raise NotImplementedError()
def __init__(self, config, relpath, source, dest):
+ self.config = config
+ self.relpath = relpath
self.source = source
self.dest = dest
- self.relpath = relpath
- self.config = config
def __repr__(self):
return self.__class__.__name__+": "+self.relpath+"/"+self.source+" => "+self.dest
@@ -115,6 +116,7 @@ class EmptyAction(Action):
"""
Ensures the destination file exists.
Creates an empty one if not.
+ TODO it should be a CopyAction with a "do not erase" parameter
"""
matched = re.compile("\.empty$")
@@ -136,15 +138,57 @@ class EmptyAction(Action):
raise ActionException(self, "Destination is a broken link")
else:
self._makedirs()
- open(dest, "w").close()
- print "Created new empty file: "+dest
+ with open(dest, "w") as destfile:
+ print "Created new empty file: "+destfile.name
def __repr__(self):
return self.__class__.__name__+": EMPTY => "+self.dest
-class CopyAction(Action):
- pass #TODO
+class TextAction(Action):
+ def check(self):
+ """
+ This action can't be invoked by a file;
+ the source parameter is used to provide the text.
+ It is used by a ProgrammableAction.
+ """
+ if self.source is not None:
+ self.text = self.source
+ self.source = None
+
+ def sync(self):
+ """
+ Write the file only if necessary
+ """
+ dest = self.dest_path()
+ if osp.islink(dest):
+ raise ActionException(self, "Destination is a link")
+ else:
+ with open(dest, "a+") as destfile:
+ if destfile.read() != self.text:
+ destfile.truncate()
+ destfile.write(self.text)
+
+ def __repr__(self):
+ return self.__class__.__name__+": TEXT => "+self.dest
+
+
+class CopyAction(TextAction):
+ matched = re.compile("\.copy$")
+
+ @classmethod
+ def matches(cls, filename):
+ if cls.matched.search(filename):
+ return cls.matched.sub("", filename)
+ return False
+
+ def text(self):
+ """
+ Retrieve the text from the source file
+ """
+ source = self.source_path()
+ with open(source, "r") as sourcefile:
+ self.text = sourcefile.read()
class Forwarder(Exception):
@@ -165,6 +209,15 @@ class EmptyForwarder(Forwarder):
return EmptyAction(parent.config, parent.relpath, None, parent.dest)
+class TextForwarder(Forwarder):
+ def __init__(self, text):
+ self.text = text
+
+ def get_proxy(self, parent):
+ # The text is passed as source
+ return TextAction(parent.config, parent.relpath, self.text, parent.dest)
+
+
class IgnoreForwarder(Forwarder):
def get_proxy(self, parent):
return None
@@ -195,6 +248,23 @@ class ProgrammableAction(Action):
def ignore():
raise IgnoreForwarder()
+ class ConfmanTemplate(Template):
+ def render(self, _strict = True, _warning = True, **kws):
+ if _warning and not kws.has_key("warning"):
+ kws["warning"] = "WARNING: Do not edit this file, edit the template instead."
+ if _strict:
+ raise TextForwarder(self.substitute(kws))
+ raise TextForwarder(self.safe_substitute(kws))
+
+ def template(name):
+ source = osp.join(osp.dirname(self.source_path()), "_"+name)
+ with open(source) as handle:
+ text = handle.read()
+ return ConfmanTemplate(text)
+
+ def text(text):
+ return ConfmanTemplate(text)
+
return locals()
def check(self):
@@ -245,6 +315,7 @@ class ConfigSource(object):
ProgrammableAction,
IgnoreAction,
EmptyAction,
+ CopyAction,
SymlinkAction,
]
diff --git a/samples/_template2 b/samples/_template2
new file mode 100644
index 0000000..c87ed22
--- /dev/null
+++ b/samples/_template2
@@ -0,0 +1,2 @@
+HELLO
+$WORLD
diff --git a/samples/copyfile.copy b/samples/copyfile.copy
new file mode 100644
index 0000000..cabc14c
--- /dev/null
+++ b/samples/copyfile.copy
@@ -0,0 +1 @@
+Kopimi
diff --git a/samples/template1.p.py b/samples/template1.p.py
new file mode 100644
index 0000000..2c6776f
--- /dev/null
+++ b/samples/template1.p.py
@@ -0,0 +1,4 @@
+text("""# $warning
+
+hello=world
+""").render()
diff --git a/samples/template2.p.py b/samples/template2.p.py
new file mode 100644
index 0000000..eb0d0b8
--- /dev/null
+++ b/samples/template2.p.py
@@ -0,0 +1 @@
+template("template2").render(_strict = False, _warning = False)