[Zope3-checkins] CVS: Zope3/src/zope/app/fssync - __init__.py:1.1.2.1 classes.py:1.1.2.1 configure.zcml:1.1.2.1 fsdirective.py:1.1.2.1 fsregistry.py:1.1.2.1 meta.zcml:1.1.2.1 syncer.py:1.1.2.1
Guido van Rossum
guido@python.org
Thu, 1 May 2003 16:13:20 -0400
Update of /cvs-repository/Zope3/src/zope/app/fssync
In directory cvs.zope.org:/tmp/cvs-serv5599/fssync
Added Files:
Tag: fssync-branch
__init__.py classes.py configure.zcml fsdirective.py
fsregistry.py meta.zcml syncer.py
Log Message:
First rough and tumble conversion of filesystem synchronization.
Don't expect this to work, but it does implement.
=== Added File Zope3/src/zope/app/fssync/__init__.py ===
#
# This file is necessary to make this directory a package.
=== Added File Zope3/src/zope/app/fssync/classes.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.
#
##############################################################################
"""Filesystem synchronization classes.
$Id: classes.py,v 1.1.2.1 2003/05/01 20:13:19 gvanrossum Exp $
"""
import os
from zope.app.content.file import File
from zope.app.content.folder import Folder
from zope.app.interfaces.fssync import IFSAddView, IObjectFile
from zope.component.interfaces import IPresentationRequest
from zope.xmlpickle.xmlpickle import dumps
class FSAddView(object):
"""See IFSAddView."""
__implements__ = IFSAddView
def __init__(self, context, request):
self.context = context
self.request = request
class AddView(FSAddView):
"""Supports to create a file system representation of zope
file type objects
"""
def create(self, fs_path=None):
if os.path.isdir(fs_path):
return Folder()
else:
return File()
class AttrMapping(object):
"""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)
class ObjectEntryAdapter(object):
"""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__)
class Default(ObjectEntryAdapter):
"""Default File-system representation for objects."""
__implements__ = IObjectFile
def getBody(self):
"See IObjectFile"
if type(self.context) is str:
return self.context
return dumps(self.context)
def setBody(self, body):
pass
def factory(self):
"See IObjectEntry"
# We have no factory, cause we're a pickle.
return None
class FSAddRequest(object):
"""XXX docstring???"""
__implements__ = IPresentationRequest
def getPresentationType(self):
return IFSAddView
def getPresentationSkin(self):
return 'default'
=== Added File Zope3/src/zope/app/fssync/configure.zcml ===
<zopeConfigure xmlns="http://namespaces.zope.org/zope"
xmlns:FSSync="http://namespaces.zope/org/FSSync"
>
<serviceType id='FSRegistryService'
interface='.IGlobalFSSyncService.' />
<service serviceType='FSRegistryService'
component='.FSRegistry.fsRegistry' />
<view
factory = ".AddView."
for = "Zope.App.FSSync.IContentDirectory."
type = "Zope.App.FSSync.IFSAddView."
name = "."
/>
</zopeConfigure>
=== Added File Zope3/src/zope/app/fssync/fsdirective.py ===
##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
""" Register class directive.
$Id: fsdirective.py,v 1.1.2.1 2003/05/01 20:13:19 gvanrossum Exp $
"""
from zope.app.fssync.fsregistry import provideSynchronizer
from zope.configuration.action import Action
def registerFSRegistry(_context, class_=None, factory=None):
"""registerFSRegistry method to register Class and Serializer factory
associated with it.
"""
cls = None
if class_ is not None:
cls = _context.resolve(class_)
fct = _context.resolve(factory)
return [Action(discriminator=('adapter', class_),
callable=provideSynchronizer,
args=(cls, fct))]
=== Added File Zope3/src/zope/app/fssync/fsregistry.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.
#
##############################################################################
"""Filesystem synchronization registry.
$Id: fsregistry.py,v 1.1.2.1 2003/05/01 20:13:19 gvanrossum Exp $
"""
from zope.app.interfaces.fssync import IGlobalFSSyncService
from zope.exceptions import DuplicationError, NotFoundError
class FSRegistry(object):
"""Registry Wrapper class.
This is a maping from Class -> Serializer Factory Method.
"""
__implements__ = IGlobalFSSyncService
def __init__(self):
self._class_factory_reg = {}
def __call__(self):
return self.__init__()
def getSynchronizer(self, object):
"""Return factory method for a given class.
The factory is returned of the object if None of the
Factory method is present return default factory.
"""
factory = self._class_factory_reg.get(object.__class__)
if factory is None:
factory = self._class_factory_reg.get(None)
if factory is None:
raise NotFoundError
return factory(object)
def provideSynchronizer(self,class_, factory):
"""Set class_, factory into the dictionary."""
if class_ in self._class_factory_reg:
raise DuplicationError
else:
self._class_factory_reg[class_] = factory
_clear = __init__
# The FS registry serializer service instance
fsRegistry = FSRegistry()
provideSynchronizer = fsRegistry.provideSynchronizer
getSynchronizer = fsRegistry.getSynchronizer
_clear = fsRegistry._clear
# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
from zope.testing.cleanup import addCleanUp
addCleanUp(_clear)
del addCleanUp
=== Added File Zope3/src/zope/app/fssync/meta.zcml ===
<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
<directives namespace="http://namespaces.zope.org/FSSync">
<directive name="adapter"
attributes="class_ factory"
handler=".FSDirective.registerFSRegistry"/>
</directives>
</zopeConfigure>
=== Added File Zope3/src/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.
#
##############################################################################
"""Filesystem synchronization functions.
$Id: syncer.py,v 1.1.2.1 2003/05/01 20:13:19 gvanrossum Exp $
"""
import os, string
from zope.component import queryAdapter, getService
from zope.xmlpickle.xmlpickle import dumps, loads
from zope.app.interfaces.fssync \
import IObjectEntry, IObjectDirectory, IObjectFile
from zope.app.interfaces.annotation import IAnnotations
from zope.app.interfaces.container import IContainer
from zope.configuration.name import resolve
from zope.app.fssync.classes import Default
from zope.app.traversing import getPath
from zope.app.fssync.fsregistry import getSynchronizer
def toFS(ob, name, location, mode=None, objpath=None):
"""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
"""
objectPath = ''
# 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
syncService = getService(ob, 'FSRegistryService')
adapter = syncService.getSynchronizer(ob)
entries[name] = {'type': adapter.typeIdentifier(),
'factory': adapter.factory(),
}
try:
if mode == 'N' or mode == 'D':
objectPath = objpath
entries[name]['isNew'] = 'Y'
else:
objectPath = str(getPath(ob))
entries[name]['path'] = objectPath
except TypeError:
pass
# 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):
data = ''
if mode !='C': # is None:
if os.path.exists(path):
f = open(path, 'r')
data = f.read()
f.close()
open(path, 'w').write(string.strip(data))
else:
open(path, 'w').write(string.strip(adapter.getBody()))
if objectPath:
print 'U %s' % (objectPath[1:])
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)
if data:
open(original_path, 'w').write(string.strip(data))
else:
open(original_path, 'w').write(string.strip(adapter.getBody()))
else:
# Directory
if objectPath:
print 'UPDATING %s' % (objectPath[1:])
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({}))
elif mode == 'D':
admin_dir = os.path.join(path, '@@Zope')
os.mkdir(admin_dir)
open(dir_entries, 'w').write(dumps({}))
else:
os.mkdir(path)
if mode == 'D':
admin_dir = os.path.join(path, '@@Zope')
os.mkdir(admin_dir)
dir_entries = os.path.join(path, '@@Zope', 'Entries.xml')
open(dir_entries, 'w').write(dumps({}))
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, mode=None):
"""Synchromize a file from what's on the file system.
"""
msg =''
objectPath = ''
# 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
ob = container[name]
syncService = getService(ob, 'FSRegistryService')
adapter = syncService.getSynchronizer(ob)
# 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:
if entry.get('type') == '__builtin__.str':
newOb = open(path).read()
_setItem(container, name, newOb, old=1)
else:
# Special case pickle data
oldOb = container[name]
newOb = loads(open(path).read())
try:
# See if we can and 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]
syncService = getService(ob, 'FSRegistryService')
adapter = syncService.getSynchronizer(ob)
# 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, mode)
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, mode)
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())
if mode is not None and mode != 'T':
if string.find(path,'@@Zope')==-1:
#copying to original
fspath = path
f = open(fspath, 'r')
data = f.read()
f.close()
original_path = os.path.join(os.path.dirname(fspath),'@@Zope','Original',os.path.basename(fspath))
f = open(original_path, 'w')
f.write(string.strip(data))
f.close()
entries_path = os.path.join(os.path.dirname(fspath),'@@Zope','Entries.xml')
entries = loads(open(entries_path).read())
if entries[os.path.basename(fspath)].has_key('isNew'):
del entries[os.path.basename(fspath)]['isNew']
open(entries_path, 'w').write(dumps(entries))
objectpath = entries[os.path.basename(fspath)]['path']
msg = "%s <-- %s" %(objectpath, string.split(objectpath,'/')[-1])
print msg
else:
# Directory
if not os.path.isdir(path):
raise SynchronizationError("Object is directory, but data is file")
if mode != 'T':
entries_path = os.path.join(os.path.dirname(path),'@@Zope','Entries.xml')
entries = loads(open(entries_path).read())
if entries[os.path.basename(path)].has_key('isNew'):
del entries[os.path.basename(path)]['isNew']
open(entries_path, 'w').write(dumps(entries))
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, mode)