[Zope3-checkins] CVS: Zope3/src/zope/fssync - copier.py:1.1
fsmerger.py:1.20 fssync.py:1.46 main.py:1.37 metadata.py:1.11
Fred L. Drake, Jr.
fred at zope.com
Fri Sep 5 16:09:37 EDT 2003
Update of /cvs-repository/Zope3/src/zope/fssync
In directory cvs.zope.org:/tmp/cvs-serv18320
Modified Files:
fsmerger.py fssync.py main.py metadata.py
Added Files:
copier.py
Log Message:
Implement a "zsync copy" command that handles copying of objects, including
the extra and annotation data.
=== Added File Zope3/src/zope/fssync/copier.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Tree-copy helpers for 'zsync copy' and 'zbundle create'.
$Id: copier.py,v 1.1 2003/09/05 19:09:36 fdrake Exp $
"""
import os
import shutil
from zope.fssync import fsutil
class FileCopier:
"""Copy from a normal file tree into an fssync checkout."""
def __init__(self, sync):
self.sync = sync
self.ignore = sync.fsmerger.ignore
def copy(self, source, target, children=True):
if os.path.isdir(source):
os.mkdir(target)
shutil.copymode(source, target)
self.addEntry(source, target)
if children:
queue = self.listDirectory(source)
while queue:
fn = queue.pop(0)
src = os.path.join(source, fn)
dst = os.path.join(target, fn)
if os.path.isdir(src):
os.mkdir(dst)
shutil.copymode(src, dst)
self.addEntry(src, dst)
queue.extend([os.path.join(fn, f)
for f in self.listDirectory(src)])
else:
shutil.copy(src, dst)
self.addEntry(src, dst)
else:
shutil.copy(source, target)
self.addEntry(source, target)
def addEntry(self, source, target):
self.sync.add(target)
def listDirectory(self, dir):
return [fn
for fn in os.listdir(dir)
if fn != "@@Zope"
if not self.sync.fsmerger.ignore(fn)]
class ObjectCopier(FileCopier):
"""Copy objects from an fssync checkout into an fssync checkout."""
def addEntry(self, source, target):
type, factory = self.sync.metadata.gettypeinfo(source)
self._syncadd(target, type, factory)
self._copyspecials(source, target, fsutil.getextra)
self._copyspecials(source, target, fsutil.getannotations)
def _syncadd(self, target, type, factory):
self.sync.add(target, type, factory)
def _copyspecials(self, source, target, getwhat):
src = getwhat(source)
if os.path.isdir(src):
dst = getwhat(target)
fsutil.ensuredir(dst)
copier = SpecialCopier(self.sync)
for name in self.sync.metadata.getnames(src):
# copy a single child
copier.copy(os.path.join(src, name), os.path.join(dst, name))
self.sync.metadata.flush()
def listDirectory(self, dir):
# We don't need to worry about fsmerger.ignore() since we're
# only relying on metadata to generate the list of names.
return self.sync.metadata.getnames(dir)
class SpecialCopier(ObjectCopier):
"""Copy extras and annotations as part of an object copy.
This is a specialized copier that doesn't expect the original to
have a path.
"""
def _syncadd(self, target, type, factory):
self.sync.basicadd(target, type, factory)
=== Zope3/src/zope/fssync/fsmerger.py 1.19 => 1.20 ===
--- Zope3/src/zope/fssync/fsmerger.py:1.19 Thu Sep 4 10:59:32 2003
+++ Zope3/src/zope/fssync/fsmerger.py Fri Sep 5 15:09:36 2003
@@ -57,24 +57,25 @@
# XXX probably for the best; we *don't* know the right
# thing to do anyway
return
- self.merge_extra(local, remote)
- self.merge_annotations(local, remote)
+ flag = self.metadata.getentry(local).get("flag")
+ self.merge_extra(local, remote, flag)
+ self.merge_annotations(local, remote, flag)
if not exists(local) and not self.metadata.getentry(local):
self.remove_special(local, "Extra")
self.remove_special(local, "Annotations")
self.remove_special(local, "Original")
- def merge_extra(self, local, remote):
+ def merge_extra(self, local, remote, flag):
"""Helper to merge the Extra trees."""
lextra = fsutil.getextra(local)
rextra = fsutil.getextra(remote)
- self.merge_dirs(lextra, rextra)
+ self.merge_dirs(lextra, rextra, flag=flag, special=True)
- def merge_annotations(self, local, remote):
+ def merge_annotations(self, local, remote, flag):
"""Helper to merge the Anotations trees."""
lannotations = fsutil.getannotations(local)
rannotations = fsutil.getannotations(remote)
- self.merge_dirs(lannotations, rannotations)
+ self.merge_dirs(lannotations, rannotations, flag=flag, special=True)
def remove_special(self, local, what):
"""Helper to remove an Original, Extra or Annotations file/tree."""
@@ -86,7 +87,7 @@
else:
# XXX when should this ever happen?
os.remove(target)
- # remove the specials directory if it's empty
+ # remove the specials directory only if it's empty
if isdir(dir):
try:
os.rmdir(dir)
@@ -108,7 +109,7 @@
action, state) or state
self.reportaction(action, state, local)
- def merge_dirs(self, localdir, remotedir):
+ def merge_dirs(self, localdir, remotedir, flag=None, special=False):
"""Merge remote directory into local directory."""
lentrynames = self.metadata.getnames(localdir)
rentrynames = self.metadata.getnames(remotedir)
@@ -119,7 +120,7 @@
if not lentry:
if not rentry:
- if exists(localdir):
+ if exists(localdir) and not special:
self.reportdir("?", localdir)
else:
if not exists(localdir):
@@ -152,11 +153,14 @@
self.clear_dir(localdir)
return
+ if not special:
+ flag = lentry.get("flag")
if exists(localdir):
- if lentry.get("flag") == "added":
+ if flag == "added":
if exists(remotedir):
self.reportdir("U", localdir)
- del lentry["flag"]
+ if "flag" in lentry:
+ del lentry["flag"]
else:
self.reportdir("A", localdir)
else:
@@ -169,13 +173,13 @@
# remote versions are gone, unless there have
# been local changes.
self.merge(join(localdir, name), join(remotedir, name))
- self.clear_dir(localdir)
+ if flag != "added":
+ self.clear_dir(localdir)
return
lnames = dict([(normcase(name), name)
for name in os.listdir(localdir)])
else:
- flag = lentry.get("flag")
if flag == "removed":
self.reportdir("R", localdir)
return # There's no point in recursing down!
=== Zope3/src/zope/fssync/fssync.py 1.45 => 1.46 ===
--- Zope3/src/zope/fssync/fssync.py:1.45 Fri Aug 29 08:47:36 2003
+++ Zope3/src/zope/fssync/fssync.py Fri Sep 5 15:09:36 2003
@@ -496,6 +496,8 @@
# XXX how to recurse?
self.dirrevert(target)
self.metadata.flush()
+ if os.path.isdir(target):
+ target = join(target, "")
self.reporter("Reverted " + target)
def dirrevert(self, target):
@@ -587,6 +589,35 @@
if factory:
entry["factory"] = factory
return entry
+
+ def copy(self, src, dst=None, children=True):
+ if not exists(src):
+ raise Error("%s does not exist" % src)
+ dst = dst or ''
+ if (not dst) or isdir(dst):
+ target_dir = dst
+ target_name = basename(os.path.abspath(src))
+ else:
+ target_dir, target_name = os.path.split(dst)
+ if target_dir:
+ if not exists(target_dir):
+ raise Error("destination directory does not exist: %r"
+ % target_dir)
+ if not isdir(target_dir):
+ import errno
+ err = IOError(errno.ENOTDIR, "Not a directory", target_dir)
+ raise Error(str(err))
+ if not self.metadata.getentry(target_dir):
+ raise Error("nothing known about '%s'" % target_dir)
+ srcentry = self.metadata.getentry(src)
+ from zope.fssync import copier
+ if srcentry:
+ # already known to fssync; we need to deal with metadata,
+ # Extra, and Annotations
+ copier = copier.ObjectCopier(self)
+ else:
+ copier = copier.FileCopier(self)
+ copier.copy(src, join(target_dir, target_name), children)
def mkdir(self, path):
dir, name = split(path)
=== Zope3/src/zope/fssync/main.py 1.36 => 1.37 ===
--- Zope3/src/zope/fssync/main.py:1.36 Wed Aug 27 15:36:21 2003
+++ Zope3/src/zope/fssync/main.py Fri Sep 5 15:09:36 2003
@@ -16,17 +16,20 @@
Command line syntax summary:
-%(program)s help [COMMAND ...]
-%(program)s checkout [local_options] URL [TARGETDIR]
-%(program)s update [local_options] [TARGET ...]
-%(program)s commit [local_options] [TARGET ...]
-%(program)s diff [local_options] [TARGET ...]
-%(program)s status [local_options] [TARGET ...]
-%(program)s add [local_options] PATH ...
-%(program)s mkdir [local_options] PATH ...
-%(program)s remove [local_options] TARGET ...
-%(program)s resolve [local_options] PATH ...
-%(program)s checkin [local_options] URL [TARGETDIR]
+%(program)s add [options] PATH ...
+%(program)s checkin [options] URL [TARGETDIR]
+%(program)s checkout [options] URL [TARGETDIR]
+%(program)s commit [options] [TARGET ...]
+%(program)s copy [options] SOURCE [TARGET]
+%(program)s diff [options] [TARGET ...]
+%(program)s login [options] URL
+%(program)s logout [options] URL
+%(program)s mkdir PATH ...
+%(program)s remove [options] TARGET ...
+%(program)s resolve PATH ...
+%(program)s revert PATH ...
+%(program)s status [TARGET ...]
+%(program)s update [TARGET ...]
``%(program)s help'' prints the global help (this message)
``%(program)s help command'' prints the local help for the command
@@ -150,6 +153,36 @@
for a in args:
fs.add(a, type, factory)
+def copy(opts, args):
+ """%(program)s copy [-l | -R] SOURCE [TARGET]
+
+ """
+ recursive = None
+ for o, a in opts:
+ if o in ("-l", "--local"):
+ if recursive:
+ raise Usage("%r conflicts with %r" % (o, recursive))
+ recursive = False
+ elif o in ("-R", "--recursive"):
+ if recursive is False:
+ raise Usage("%r conflicts with -l" % o)
+ recursive = o
+ if not args:
+ raise Usage("copy requires at least one argument")
+ if len(args) > 2:
+ raise Usage("copy allows at most two arguments")
+ source = args[0]
+ if len(args) == 2:
+ target = args[1]
+ else:
+ target = None
+ if recursive is None:
+ recursive = True
+ else:
+ recursive = bool(recursive)
+ fs = FSSync()
+ fs.copy(source, target, children=recursive)
+
def remove(opts, args):
"""%(program)s remove TARGET ...
@@ -321,6 +354,7 @@
(checkin, "", "F:m:", "file= message="),
(checkout, "co", "", ""),
(commit, "ci", "F:m:r", "file= message= raise-on-conflicts"),
+ (copy, "cp", "lR", "local recursive"),
(diff, "di", "bBcC:iNuU:", "brief context= unified="),
(login, "", "u:", "user="),
(logout, "", "u:", "user="),
=== Zope3/src/zope/fssync/metadata.py 1.10 => 1.11 ===
--- Zope3/src/zope/fssync/metadata.py:1.10 Sun Aug 17 02:08:56 2003
+++ Zope3/src/zope/fssync/metadata.py Fri Sep 5 15:09:36 2003
@@ -59,6 +59,10 @@
dir, base = split(file)
return self.getmanager(dir).getentry(base)
+ def gettypeinfo(self, path):
+ entry = self.getentry(path)
+ return entry.get("type"), entry.get("factory")
+
def getmanager(self, dir):
dir = realpath(dir)
key = normcase(dir)
More information about the Zope3-Checkins
mailing list