[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/FSSync - AttrMapping.py:1.1.2.1 Default.py:1.1.2.1 IObjectDirectory.py:1.1.2.1 IObjectEntry.py:1.1.2.1 IObjectFile.py:1.1.2.1 ObjectEntryAdapter.py:1.1.2.1 Syncer.py:1.1.2.1 __init__.py:1.1.2.1 configure.zcml:1.1.2.1
Jim Fulton
jim@zope.com
Thu, 10 Oct 2002 00:43:27 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/App/FSSync
In directory cvs.zope.org:/tmp/cvs-serv16171/lib/python/Zope/App/FSSync
Added Files:
Tag: FileSystemSync-branch
AttrMapping.py Default.py IObjectDirectory.py IObjectEntry.py
IObjectFile.py ObjectEntryAdapter.py Syncer.py __init__.py
configure.zcml
Log Message:
Initial exploratory prototype code
=== Added File Zope3/lib/python/Zope/App/FSSync/AttrMapping.py ===
##############################################################################
#
# 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: AttrMapping.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
__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__
=== Added File Zope3/lib/python/Zope/App/FSSync/Default.py ===
##############################################################################
#
# 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: Default.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
__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__
=== Added File Zope3/lib/python/Zope/App/FSSync/IObjectDirectory.py ===
##############################################################################
#
# 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: IObjectDirectory.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
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__
=== Added File Zope3/lib/python/Zope/App/FSSync/IObjectEntry.py ===
##############################################################################
#
# 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: IObjectEntry.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
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__
=== Added File Zope3/lib/python/Zope/App/FSSync/IObjectFile.py ===
##############################################################################
#
# 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: IObjectFile.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
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__
=== Added File Zope3/lib/python/Zope/App/FSSync/ObjectEntryAdapter.py ===
##############################################################################
#
# 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: ObjectEntryAdapter.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
__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__
=== Added File Zope3/lib/python/Zope/App/FSSync/Syncer.py ===
##############################################################################
#
# 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: Syncer.py,v 1.1.2.1 2002/10/10 04:43:25 jim Exp $
"""
__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)
=== Added File Zope3/lib/python/Zope/App/FSSync/__init__.py ===
##############################################################################
#
# 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.
#
##############################################################################
=== Added File Zope3/lib/python/Zope/App/FSSync/configure.zcml ===
<zopeConfigure xmlns="http://namespaces.zope.org/zope">
<adapter provides="Zope.App.FSSync.IObjectFile."
factory=".Default." />
</zopeConfigure>