[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/FSSync - AttrMapping.py:1.2 Default.py:1.2 IObjectDirectory.py:1.2 IObjectEntry.py:1.2 IObjectFile.py:1.2 ObjectEntryAdapter.py:1.2 Syncer.py:1.2 __init__.py:1.2 configure.zcml:1.2
Jim Fulton
jim@zope.com
Fri, 11 Oct 2002 02:28:36 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/App/FSSync
In directory cvs.zope.org:/tmp/cvs-serv1649/lib/python/Zope/App/FSSync
Added Files:
AttrMapping.py Default.py IObjectDirectory.py IObjectEntry.py
IObjectFile.py ObjectEntryAdapter.py Syncer.py __init__.py
configure.zcml
Log Message:
adding (back) changed to branch
=== Zope3/lib/python/Zope/App/FSSync/AttrMapping.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/AttrMapping.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+__metaclass__ = type
+
+class AttrMapping:
+ """Convenience object implementing a mapping on selected object attributes
+ """
+
+ def __init__(self, context, attrs, schema=None):
+ self.attrs = attrs
+ self.context = context
+
+ def __getitem__(self, name):
+ if name in self.attrs:
+ return getattr(self.context, name)
+ raise KeyError, name
+
+ def get(self, name, default):
+ if name in self.attrs:
+ return getattr(self.context, name, default)
+ return default
+
+ def __contains__(self, name):
+ return (name in self.attrs) and hasattr(self.context, name)
+
+ def __delitem__(self, name):
+ if name in self.attrs:
+ delattr(self.context, name)
+ return
+ raise KeyError, name
+
+ def __setitem__(self, name, value):
+ if name in self.attrs:
+ setattr(self.context, name, value)
+ return
+ raise KeyError, name
+
+ def __iter__(self):
+ return iter(self.attrs)
+
+__doc__ = AttrMapping.__doc__ + __doc__
+
=== Zope3/lib/python/Zope/App/FSSync/Default.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/Default.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,46 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+__metaclass__ = type
+
+from Zope.App.FSSync.ObjectEntryAdapter import ObjectEntryAdapter
+from Zope.App.FSSync.IObjectFile import IObjectFile
+from Zope.XMLPickle.XMLPickle import dumps
+
+
+class Default(ObjectEntryAdapter):
+ """Default File-system representation for objects
+ """
+
+ __implements__ = IObjectFile
+
+ def getBody(self):
+ "See Zope.App.FSSync.IObjectFile.IObjectFile"
+ if type(self.context) is str:
+ return self.context
+ return dumps(self.context)
+
+ def setBody(self, body):
+ pass
+
+ def factory(self):
+ "See Zope.App.FSSync.IObjectEntry.IObjectEntry"
+ # We have no factory, cause we're a pickle.
+
+
+__doc__ = Default.__doc__ + __doc__
+
=== Zope3/lib/python/Zope/App/FSSync/IObjectDirectory.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/IObjectDirectory.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+from IObjectEntry import IObjectEntry
+
+class IObjectDirectory(IObjectEntry):
+ """File-system object representation for directory-like objects
+ """
+
+ def contents():
+ """Return the contents
+
+ A sequence of name, value object are returned. The value in each
+ pair will be syncronized.
+ """
+
+__doc__ = IObjectDirectory.__doc__ + __doc__
=== Zope3/lib/python/Zope/App/FSSync/IObjectEntry.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/IObjectEntry.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+from Interface import Interface
+
+class IObjectEntry(Interface):
+ """File-system object representation
+ """
+
+ def extra():
+ """Return extra data for the entry.
+
+ The data are returned as a mapping object that allows *both*
+ data retrieval and setting. The mapping is from names to
+ objects that will be serialized to or from the file system.
+
+ """
+
+ def typeIdentifier():
+ """Return a dotted name that identifies the object type.
+
+ This is typically a dotted class name.
+
+ This is used when synchronizing from the file system to the
+ database to decide whether the existing object and the new
+ object are of the same type.
+
+ """
+
+ def factory():
+ """Return the dotted name of a factory to recreate an empty entry.
+
+ The factory will be called with no arguments. It is usually
+ the dotted name of the object class.
+
+ """
+
+__doc__ = IObjectEntry.__doc__ + __doc__
=== Zope3/lib/python/Zope/App/FSSync/IObjectFile.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/IObjectFile.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,30 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+from IObjectEntry import IObjectEntry
+
+class IObjectFile(IObjectEntry):
+ """File-system object representation for file-like objects
+ """
+
+ def getBody():
+ """Return the file body"""
+
+ def setBody():
+ """Change the file body"""
+
+__doc__ = IObjectFile.__doc__ + __doc__
=== Zope3/lib/python/Zope/App/FSSync/ObjectEntryAdapter.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/ObjectEntryAdapter.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+__metaclass__ = type
+
+class ObjectEntryAdapter:
+ """Convenience Base class for ObjectEntry adapter implementations
+ """
+
+ def __init__(self, context):
+ self.context = context
+
+ def extra(self):
+ "See Zope.App.FSSync.IObjectEntry.IObjectEntry"
+
+ def typeIdentifier(self):
+ "See Zope.App.FSSync.IObjectEntry.IObjectEntry"
+ class_ = self.context.__class__
+ return "%s.%s" % (class_.__module__, class_.__name__)
+
+ def factory(self):
+ "See Zope.App.FSSync.IObjectEntry.IObjectEntry"
+ # Return the dotted class name, assuming that it can be used
+ class_ = self.context.__class__
+ return "%s.%s" % (class_.__module__, class_.__name__)
+
+
+__doc__ = ObjectEntryAdapter.__doc__ + __doc__
+
=== Zope3/lib/python/Zope/App/FSSync/Syncer.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/Syncer.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,272 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+XXX longer description goes here.
+
+$Id$
+"""
+__metaclass__ = type
+
+import os
+from Zope.ComponentArchitecture import getAdapter, queryAdapter
+from Zope.XMLPickle.XMLPickle import dumps, loads
+from IObjectEntry import IObjectEntry
+from IObjectDirectory import IObjectDirectory
+from IObjectFile import IObjectFile
+from Zope.App.OFS.Annotation.IAnnotations import IAnnotations
+from Zope.App.OFS.Container.IContainer import IContainer
+from Zope.Configuration.name import resolve
+from Default import Default
+
+def toFS(ob, name, location):
+ """Check an object out to the file system
+
+ ob -- The object to be checked out
+
+ name -- The name of the object
+
+ location -- The directory on the file system where the object will go
+ """
+
+ # Look for location admin dir
+ admin_dir = os.path.join(location, '@@Zope')
+ if not os.path.exists(admin_dir):
+ os.mkdir(admin_dir)
+
+ # Open Entries file
+ entries_path = os.path.join(admin_dir, "Entries.xml")
+ if os.path.exists(entries_path):
+ entries = loads(open(entries_path).read())
+ else:
+ entries = {}
+
+ # Get the object adapter
+ adapter = getAdapter(ob, IObjectEntry)
+
+ entries[name] = {'type': adapter.typeIdentifier(),
+ 'factory': adapter.factory(),
+ }
+
+ # Write entries file
+ open(entries_path, 'w').write(dumps(entries))
+
+
+ # Get name path and check that name is not an absolute path
+ path = os.path.join(location, name)
+ if path == name:
+ raise ValueError("Invalid absolute path name")
+
+
+ # Handle extras
+ extra = adapter.extra()
+ if extra:
+ extra_dir = os.path.join(admin_dir, 'Extra')
+ if not os.path.exists(extra_dir):
+ os.mkdir(extra_dir)
+ extra_dir = os.path.join(extra_dir, name)
+ if not os.path.exists(extra_dir):
+ os.mkdir(extra_dir)
+ for ename in extra:
+ edata = extra[ename]
+ toFS(edata, ename, extra_dir)
+
+ # Handle annotations
+ annotations = queryAdapter(ob, IAnnotations)
+ if annotations is not None:
+ annotation_dir = os.path.join(admin_dir, 'Annotations')
+ if not os.path.exists(annotation_dir):
+ os.mkdir(annotation_dir)
+ annotation_dir = os.path.join(annotation_dir, name)
+ if not os.path.exists(annotation_dir):
+ os.mkdir(annotation_dir)
+ for key in annotations:
+ annotation = annotations[key]
+ toFS(annotation, key, annotation_dir)
+
+
+ # Handle data
+ if IObjectFile.isImplementedBy(adapter):
+ open(path, 'w').write(adapter.getBody())
+ original_path = os.path.join(admin_dir, 'Original')
+ if not os.path.exists(original_path):
+ os.mkdir(original_path)
+ original_path = os.path.join(original_path, name)
+ open(original_path, 'w').write(adapter.getBody())
+
+
+ else:
+ # Directory
+ if os.path.exists(path):
+ dir_entries = os.path.join(path, '@@Zope', 'Entries.xml')
+ if os.path.exists(dir_entries):
+ open(dir_entries, 'w').write(dumps({}))
+ else:
+ os.mkdir(path)
+
+
+ for cname, cob in adapter.contents():
+ toFS(cob, cname, path)
+
+class SynchronizationError(Exception): pass
+
+def _setItem(container, name, ob, old=0):
+ # Set an item in a container or in a mapping
+ if IContainer.isImplementedBy(container):
+ if old:
+ del container[name]
+ newName = container.setObject(name, ob)
+ if newName != name:
+ raise SynchronizationError(
+ "Container generated new name for %s" % path)
+ else:
+ # Not a container, must be a mapping
+ container[name] = ob
+
+def fromFS(container, name, location):
+ """Synchromize a file from what's on the file system.
+ """
+
+ # Look for location admin dir
+ admin_dir = os.path.join(location, '@@Zope')
+ if not os.path.exists(admin_dir):
+ raise SynchronizationError("No @@Zope admin directory, %s" % admin_dir)
+
+ # Open Entries file
+ entries_path = os.path.join(admin_dir, "Entries.xml")
+ entries = loads(open(entries_path).read())
+ entry = entries[name]
+ factory = entry.get('factory')
+
+ # Get name path and check that name is not an absolute path
+ path = os.path.join(location, name)
+ if path == name:
+ raise ValueError("Invalid absolute path name")
+
+ # See if this is an existing object
+ if name in container:
+ # Yup, let's see if we have the same kind of object
+
+ # Get the object adapter
+ adapter = getAdapter(container[name], IObjectEntry)
+
+ # Replace the object if the type is different
+ if adapter.typeIdentifier() != entry.get('type'):
+ # We have a different object, replace the one that's there
+
+ if factory:
+ newOb = resolve(factory)()
+ else:
+ newOb = loads(open(path).read())
+
+ _setItem(container, name, newOb, old=1)
+
+ elif not factory:
+ # Special case pickle data
+ oldOb = container[name]
+ newOb = loads(open(path).read())
+ try:
+ # See id we can amd should just copy the state
+ oldOb._p_oid # Is it persisteny
+ getstate = newOb.__getstate__
+ except AttributeError:
+ # Nope, we have to replace.
+ _setItem(container, name, newOb, old=1)
+ else:
+ oldOb.__setstate__(getstate())
+ oldOb._p_changed = 1
+
+
+ else:
+ # We need to create a new object
+
+ if factory:
+ newOb = resolve(entry['factory'])()
+ else:
+ newOb = loads(open(path).read())
+
+ _setItem(container, name, newOb)
+
+
+ # Get the object adapter again
+ ob = container[name]
+ adapter = getAdapter(ob, IObjectEntry)
+
+
+ # Handle extra
+ extra = adapter.extra()
+ extra_dir = os.path.join(admin_dir, 'Extra', name)
+ extra_entries_path = os.path.join(extra_dir, "@@Zope", "Entries.xml")
+ if extra:
+ if not os.path.exists(extra_entries_path):
+ # The file system has no extras, so delete all of the object's
+ # extras.
+ for key in extra:
+ del extra[key]
+ else:
+ extra_entries = loads(
+ open(extra_entries_path).read())
+ for ename in extra_entries:
+ fromFS(extra, ename, extra_dir)
+ elif os.path.exists(extra_entries_path):
+ extra_entries = loads(
+ open(extra_entries_path).read())
+ if extra_entries:
+ raise SynchronizationError(
+ "File-system extras for object with no extra data")
+
+
+ # Handle annotations
+ annotations = queryAdapter(ob, IAnnotations)
+ annotation_dir = os.path.join(admin_dir, 'Annotations', name)
+ annotation_entries_path = os.path.join(
+ annotation_dir, "@@Zope", "Entries.xml")
+ if annotations is not None:
+ if not os.path.exists(annotation_entries_path):
+ # The file system has no annotations, so delete all of the object's
+ # annotations.
+ for key in annotations:
+ del annotations[key]
+ else:
+ annotation_entries = loads(
+ open(annotation_entries_path).read())
+ for ename in annotation_entries:
+ fromFS(annotations, ename, annotation_dir)
+ elif os.path.exists(annotation_entries_path):
+ annotation_entries = loads(
+ open(annotation_entries_path).read())
+ if annotation_entries:
+ raise SynchronizationError(
+ "File-system annotations for non annotatable object")
+
+ # Handle data
+ if IObjectFile.isImplementedBy(adapter):
+ # File
+ if os.path.isdir(path):
+ raise SynchronizationError("Object is file, but data is directory")
+ adapter.setBody(open(path).read())
+
+ else:
+ # Directory
+ if not os.path.isdir(path):
+ raise SynchronizationError("Object is directory, but data is file")
+
+ dir_entries_path = os.path.join(path, '@@Zope', 'Entries.xml')
+ dir_entries = loads(open(dir_entries_path).read())
+
+ for cname in dir_entries:
+ fromFS(ob, cname, path)
+
+
+
=== Zope3/lib/python/Zope/App/FSSync/__init__.py 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/__init__.py Fri Oct 11 02:28:05 2002
@@ -0,0 +1,13 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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.
+#
+##############################################################################
=== Zope3/lib/python/Zope/App/FSSync/configure.zcml 1.1 => 1.2 ===
--- /dev/null Fri Oct 11 02:28:36 2002
+++ Zope3/lib/python/Zope/App/FSSync/configure.zcml Fri Oct 11 02:28:05 2002
@@ -0,0 +1,6 @@
+<zopeConfigure xmlns="http://namespaces.zope.org/zope">
+
+<adapter provides="Zope.App.FSSync.IObjectFile."
+ factory=".Default." />
+
+</zopeConfigure>