<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os
import repository

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self, justContentsForConfig = 0):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    if justContentsForConfig:
			if l[-1] == "-":
			    # XXX just hope for the best?
			    pass
			else:
			    flags = int(l[-1], 16)
			    if flags & files._FILE_FLAG_CONFIG:
				assert(self.hasFileContents(l[1]))
		    else:
			assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	assert(not oldVersion or oldVersion.timeStamp)
	assert(newVersion.timeStamp)
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, csPkg):
	assert(not csPkg.getOldVersion() or csPkg.getOldVersion().timeStamp)
	assert(csPkg.getNewVersion().timeStamp)

	self.newPackages[csPkg.getName()] = csPkg
	if csPkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	assert(version.timeStamp)
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages.values()

    def getNewPackage(self, name):
	return self.newPackages[name]

    def hasNewPackage(self, name):
	return self.newPackages.has_key(name)

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash, contents):
	self.fileContents[hash] = contents

    def getFileContents(self, hash):
	return self.fileContents[hash].get()

    def hasFileContents(self, hash):
	return self.fileContents.has_key(hash)

    def getFileList(self):
	return self.files.items()

    def remapPaths(self, map):
	for pkgCs in self.newPackages.values():
	    pkgCs.remapPaths(map)

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages.values():
	    pkg.formatToFile(self, cfg, f)
	for (pkgName, version) in self.oldPackages:
	    print pkgName, "removed", version.asString(cfg.defaultbranch)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def getFileOldVersion(self, fileId):
	return self.files[fileId][0]

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s\n" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents.keys():
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    # if availableFiles is set, this includes the contents that it can
    # find, but doesn't worry about files which it can't find
    def invert(self, repos, availableFiles = 0):
	assert(not self.abstract)
	# this is easy to fix if it turns out to be necessary
	assert(not self.oldPackages)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    if not pkgCs.getOldVersion():
		# this was a new package, and the inverse of a new
		# package is an old package
		inversion.oldPackage(pkgCs.getName(), pkgCs.getNewVersion())
		continue

	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    # this is a modified package and needs to be inverted

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))

		if (not availableFiles) or \
			    repos.hasFileContents(origFile.sha1()):
			cont = repository.FileContentsFromRepository(repos,
						      origFile.sha1())
			inversion.addFileContents(origFile.sha1(), cont)

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    if (not availableFiles) or \
				repos.hasFileContents(origFile.sha1()):
			cont = repository.FileContentsFromRepository(repos,
						      origFile.sha1())
			inversion.addFileContents(origFile.sha1(), cont)

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	self.newPackages = {}
	self.oldPackages = []
	self.files = {}
	self.fileContents = {}
	self.abstract = 0

class ChangeSetFromRepository(ChangeSet):

    def newPackage(self, pkg):
	# add the time stamps to the package version numbers
	if pkg.getOldVersion():
	    if not pkg.getOldVersion().timeStamp:
		pkg.changeOldVersion(self.repos.getFullVersion(pkg.getName(),
							       pkg.getOldVersion()))

	if not pkg.getNewVersion().timeStamp:
	    pkg.changeNewVersion(self.repos.getFullVersion(pkg.getName(),
							   pkg.getNewVersion()))
	ChangeSet.newPackage(self, pkg)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def addFileContents(self, hash):
	return ChangeSet.addFileContents(hash,
			    repostitory.FileContentsFromChangeSet(cs, hash))

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, version)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file, justContentsForConfig = 0):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate(justContentsForConfig)

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSet()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFileContents(hash,
			  repository.FileContentsFromFilesystem(realPath))

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion, (not oldVersion)))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<commit_msg>added ChangeSet.hasFileChange()<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os
import repository

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self, justContentsForConfig = 0):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    if justContentsForConfig:
			if l[-1] == "-":
			    # XXX just hope for the best?
			    pass
			else:
			    flags = int(l[-1], 16)
			    if flags & files._FILE_FLAG_CONFIG:
				assert(self.hasFileContents(l[1]))
		    else:
			assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	assert(not oldVersion or oldVersion.timeStamp)
	assert(newVersion.timeStamp)
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, csPkg):
	assert(not csPkg.getOldVersion() or csPkg.getOldVersion().timeStamp)
	assert(csPkg.getNewVersion().timeStamp)

	self.newPackages[csPkg.getName()] = csPkg
	if csPkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	assert(version.timeStamp)
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages.values()

    def getNewPackage(self, name):
	return self.newPackages[name]

    def hasNewPackage(self, name):
	return self.newPackages.has_key(name)

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash, contents):
	self.fileContents[hash] = contents

    def getFileContents(self, hash):
	return self.fileContents[hash].get()

    def hasFileContents(self, hash):
	return self.fileContents.has_key(hash)

    def getFileList(self):
	return self.files.items()

    def remapPaths(self, map):
	for pkgCs in self.newPackages.values():
	    pkgCs.remapPaths(map)

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages.values():
	    pkg.formatToFile(self, cfg, f)
	for (pkgName, version) in self.oldPackages:
	    print pkgName, "removed", version.asString(cfg.defaultbranch)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def getFileOldVersion(self, fileId):
	return self.files[fileId][0]

    def hasFileChange(self, fileId):
	return self.files.has_key(fileId)

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s\n" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents.keys():
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    # if availableFiles is set, this includes the contents that it can
    # find, but doesn't worry about files which it can't find
    def invert(self, repos, availableFiles = 0):
	assert(not self.abstract)
	# this is easy to fix if it turns out to be necessary
	assert(not self.oldPackages)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    if not pkgCs.getOldVersion():
		# this was a new package, and the inverse of a new
		# package is an old package
		inversion.oldPackage(pkgCs.getName(), pkgCs.getNewVersion())
		continue

	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    # this is a modified package and needs to be inverted

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))

		if (not availableFiles) or \
			    repos.hasFileContents(origFile.sha1()):
			cont = repository.FileContentsFromRepository(repos,
						      origFile.sha1())
			inversion.addFileContents(origFile.sha1(), cont)

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    if (not availableFiles) or \
				repos.hasFileContents(origFile.sha1()):
			cont = repository.FileContentsFromRepository(repos,
						      origFile.sha1())
			inversion.addFileContents(origFile.sha1(), cont)

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	self.newPackages = {}
	self.oldPackages = []
	self.files = {}
	self.fileContents = {}
	self.abstract = 0

class ChangeSetFromRepository(ChangeSet):

    def newPackage(self, pkg):
	# add the time stamps to the package version numbers
	if pkg.getOldVersion():
	    if not pkg.getOldVersion().timeStamp:
		pkg.changeOldVersion(self.repos.getFullVersion(pkg.getName(),
							       pkg.getOldVersion()))

	if not pkg.getNewVersion().timeStamp:
	    pkg.changeNewVersion(self.repos.getFullVersion(pkg.getName(),
							   pkg.getNewVersion()))
	ChangeSet.newPackage(self, pkg)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def addFileContents(self, hash):
	return ChangeSet.addFileContents(hash,
			    repostitory.FileContentsFromChangeSet(cs, hash))

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, version)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file, justContentsForConfig = 0):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate(justContentsForConfig)

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSet()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFileContents(hash,
			  repository.FileContentsFromFilesystem(realPath))

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion, (not oldVersion)))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, pkg):
	self.newPackages.append(pkg)
	if pkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash):
	self.fileContents.append(hash)

    def getFileList(self):
	return self.files.items()

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages:
	    pkg.formatToFile(self, cfg, f)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents:
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    def invert(self, repos):
	assert(not self.abstract)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))
		inversion.addFileContents(origFile.sha1())

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    inversion.addFileContents(origFile.sha1())

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	assert(self.__class__ != ChangeSet)
	self.newPackages = []
	self.oldPackages = []
	self.files = {}
	self.fileContents = []
	self.abstract = 0

class ChangeSetFromFilesystem(ChangeSet):

    def getFileContents(self, fileId):
	return open(self.fileMap[fileId])

    def hasFileContents(self, fileId):
	return self.fileMap.has_key(fileId)

    def addFilePointer(self, fileId, path):
	self.fileMap[fileId] = path

    def __init__(self):
	self.fileMap = {}
	ChangeSet.__init__(self)

class ChangeSetFromRepository(ChangeSet):

    def getFileContents(self, fileId):
	return self.repos.pullFileContentsObject(fileId)

    def hasFileContents(self, fileId):
	return self.repos.hasFileContents(fileId)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def getFileContents(self, fileId):
	return self.absCS.getFileContents(fileId)

    def hasFileContents(self, fileId):
	return self.absCS.hasFileContents(fileId)

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, verStr)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate()

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSetFromFilesystem()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, realPath)

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<commit_msg>removed packages weren't being written or parsed quite right<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, pkg):
	self.newPackages.append(pkg)
	if pkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash):
	self.fileContents.append(hash)

    def getFileList(self):
	return self.files.items()

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages:
	    pkg.formatToFile(self, cfg, f)
	for (pkgName, version) in self.oldPackages:
	    print pkgName, "removed", version.asString(cfg.defaultbranch)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s\n" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents:
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    def invert(self, repos):
	assert(not self.abstract)
	# this is easy to fix if it turns out to be necessary
	assert(not self.oldPackages)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    if not pkgCs.getOldVersion():
		# this was a new package, and the inverse of a new
		# package is an old package
		inversion.oldPackage(pkgCs.getName(), pkgCs.getNewVersion())
		continue

	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    # this is a modified package and needs to be inverted

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))
		inversion.addFileContents(origFile.sha1())

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    inversion.addFileContents(origFile.sha1())

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	assert(self.__class__ != ChangeSet)
	self.newPackages = []
	self.oldPackages = []
	self.files = {}
	self.fileContents = []
	self.abstract = 0

class ChangeSetFromFilesystem(ChangeSet):

    def getFileContents(self, fileId):
	return open(self.fileMap[fileId])

    def hasFileContents(self, fileId):
	return self.fileMap.has_key(fileId)

    def addFilePointer(self, fileId, path):
	self.fileMap[fileId] = path

    def __init__(self):
	self.fileMap = {}
	ChangeSet.__init__(self)

class ChangeSetFromRepository(ChangeSet):

    def getFileContents(self, fileId):
	return self.repos.pullFileContentsObject(fileId)

    def hasFileContents(self, fileId):
	return self.repos.hasFileContents(fileId)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def getFileContents(self, fileId):
	return self.absCS.getFileContents(fileId)

    def hasFileContents(self, fileId):
	return self.absCS.hasFileContents(fileId)

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, version)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate()

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSetFromFilesystem()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, realPath)

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<|endoftext|>


<commit_before>#!/usr/bin/python
# coding=utf-8

from mock import patch
from mock import Mock

from test import unittest
import configobj
import logging

from diamond.collector import Collector
from diamond.error import DiamondException

class BaseCollectorTest(unittest.TestCase):

    def tearDown(self):
        log = logging.getLogger("diamond.Collector")
        log.removeHandler(log.handlers[0])
        # Ensure that we aren't printing log messages to stdout in unit tests
        log.propagate = False

    def config_object(self):
        config = configobj.ConfigObj()
        config['server'] = {}
        config['server']['collectors_config_path'] = ''
        config['collectors'] = {}
        return config

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_SetDimensions(self, mock_publish):
        c = Collector(self.config_object(), [])
        dimensions = {
            'dim1': 'alice',
            'dim2': 'chains',
        }
        c.dimensions = dimensions
        c.publish('metric1', 1)

        for call in mock_publish.mock_calls:
            name, args, kwargs = call
            metric = args[0]
            self.assertEquals(metric.dimensions, dimensions)
        self.assertEqual(c.dimensions, None)

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_successful_error_metric(self, mock_publish):
        c = Collector(self.config_object(), [])
        mock_socket = Mock()
        c._socket = mock_socket
        with patch.object(c, 'log'):
            try:
                c.publish('metric', "bar")
            except DiamondException:
                pass
        for call in mock_publish.mock_calls:
            name, args, kwargs = call
            metric = args[0]
            self.assertEqual(metric.path, "servers.Collector.fullerite.collector_errors")

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_failed_error_metric_publish(self, mock_publish):
        c = Collector(self.config_object(), [])
        self.assertFalse(c.can_publish_metric())
        with patch.object(c, 'log'):
            try:
                c.publish('metric', "baz")
            except DiamondException:
                pass
        self.assertEquals(len(mock_publish.mock_calls), 0)

    def test_can_publish_metric(self):
        c = Collector(self.config_object(), [])
        self.assertFalse(c.can_publish_metric())

        c._socket = "socket"
        self.assertTrue(c.can_publish_metric())

    @patch('diamond.collector.Collector.flush', autoSpec=True)
    def test_batch_size_flush(self, mock_flush):
        c = Collector(self.config_object(), [])
        mock_socket = Mock()
        c._socket = mock_socket
        c.config['max_buffer_size'] = 2
        with patch.object(c, 'log'):
            try:
                c.publish('metric1', 1)
                c.publish('metric2', 2)
                c.publish('metric3', 3)
            except DiamondException:
                pass
        self.assertEquals(len(mock_flush.mock_calls), 1)
<commit_msg>Fix unit test for mocked calls<commit_after>#!/usr/bin/python
# coding=utf-8

from mock import patch
from mock import Mock

from test import unittest
import configobj
import logging

from diamond.collector import Collector
from diamond.error import DiamondException

class BaseCollectorTest(unittest.TestCase):

    def tearDown(self):
        log = logging.getLogger("diamond.Collector")
        log.removeHandler(log.handlers[0])
        # Ensure that we aren't printing log messages to stdout in unit tests
        log.propagate = False

    def config_object(self):
        config = configobj.ConfigObj()
        config['server'] = {}
        config['server']['collectors_config_path'] = ''
        config['collectors'] = {}
        return config

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_SetDimensions(self, mock_publish):
        c = Collector(self.config_object(), [])
        dimensions = {
            'dim1': 'alice',
            'dim2': 'chains',
        }
        c.dimensions = dimensions
        c.publish('metric1', 1)

        for call in mock_publish.mock_calls:
            name, args, kwargs = call
            metric = args[0]
            self.assertEquals(metric.dimensions, dimensions)
        self.assertEqual(c.dimensions, None)

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_successful_error_metric(self, mock_publish):
        c = Collector(self.config_object(), [])
        mock_socket = Mock()
        c._socket = mock_socket
        with patch.object(c, 'log'):
            try:
                c.publish('metric', "bar")
            except DiamondException:
                pass
        for call in mock_publish.mock_calls:
            name, args, kwargs = call
            metric = args[0]
            self.assertEqual(metric.path, "servers.Collector.fullerite.collector_errors")

    @patch('diamond.collector.Collector.publish_metric', autoSpec=True)
    def test_failed_error_metric_publish(self, mock_publish):
        c = Collector(self.config_object(), [])
        self.assertFalse(c.can_publish_metric())
        with patch.object(c, 'log'):
            try:
                c.publish('metric', "baz")
            except DiamondException:
                pass
        self.assertEquals(len(mock_publish.mock_calls), 0)

    def test_can_publish_metric(self):
        c = Collector(self.config_object(), [])
        self.assertFalse(c.can_publish_metric())

        c._socket = "socket"
        self.assertTrue(c.can_publish_metric())

    def test_batch_size_flush(self):
        c = Collector(self.config_object(), [])
        mock_socket = Mock()
        c._socket = mock_socket
        c._reconnect = False
        c.config['max_buffer_size'] = 2
        with patch.object(c, 'log'):
            try:
                c.publish('metric1', 1)
                c.publish('metric2', 2)
                c.publish('metric3', 3)
            except DiamondException:
                pass
        self.assertEquals(mock_socket.sendall.call_count, 1)
        self.assertEquals(len(c.payload), 1)
<|endoftext|>


<commit_before>#
# spyne - Copyright (C) Spyne contributors.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
#

"""
The ``spyne.protoco.soap.soap1_2`` module contains the implementation of a
subset of the Soap 1.2 standard.
"""

import logging

from lxml.builder import E

from spyne.protocol.soap.soap11 import Soap11
from spyne.protocol.xml import _append
from spyne.util.six import string_types
from spyne.util.etreeconv import root_dict_to_etree
from spyne.const.xml_ns import soap12_env, const_prefmap

logger = logging.getLogger(__name__)
logger_invalid = logging.getLogger(__name__ + ".invalid")

class Soap12(Soap11):
    """
    The base implementation of a subset of the Soap 1.2 standard. The
    document is available here: http://www.w3.org/TR/soap12/
    """
    mime_type = 'application/soap+xml; charset=utf-8'

    soap_env = const_prefmap[soap12_env]
    ns_soap_env = soap12_env

    def generate_subcode(self, value, subcode=None):
        subcode_node = E("{%s}Subcode" % self.soap_env)
        subcode_node.append(E("{%s}Value" % self.soap_env, value))
        if subcode:
            subcode_node.append(subcode)
        return subcode_node

    def gen_fault_codes(self, faultstring):
        faultstrings = faultstring.split('.')
        value = faultstrings.pop(0)

        if value == 'Client':
            value = 'Sender'
        elif value == 'Server':
            value = 'Receiver'
        else:
            raise TypeError('Wrong fault code, got', type(faultstring))

        return value, faultstrings

    def generate_faultcode(self, element):
        nsmap = element.nsmap
        faultcode = []
        faultcode.append(element.find('soap:Code/soap:Value', namespaces=nsmap).text)
        subcode = element.find('soap:Code/soap:Subcode', namespaces=nsmap)
        while subcode:
            faultcode.append(subcode.find('soap:Value', namespaces=nsmap).text)
            subcode = subcode.find('soap:Subcode', namespaces=nsmap)

        return '.'.join(faultcode)

    def fault_to_parent(self, ctx, cls, inst, parent, ns, *args, **kwargs):
        tag_name = "{%s}Fault" % self.ns_soap_env

        subelts = [
            E("{%s}Reason" % self.soap_env, inst.faultstring),
            E("{%s}Role" % self.soap_env, inst.faultactor),
        ]

        if isinstance(inst.faultcode, string_types):
            value, faultcodes  = self.gen_fault_codes(inst.faultcode)

            code = E("{%s}Code" % self.soap_env)
            code.append(E("{%s}Value" % self.soap_env, value))

            child_subcode = 0
            for value in faultcodes:
                if child_subcode:
                    child_subcode = self.generate_subcode(value, child_subcode)
                else:
                    child_subcode = self.generate_subcode(value)
            code.append(child_subcode)

            _append(subelts, code)

        if isinstance(inst.detail, dict):
            _append(subelts, E('{%s}Detail' % self.soap_env, root_dict_to_etree(inst.detail)))

        elif inst.detail is None:
            raise TypeError('Fault detail Must be dict, got', type(inst.detail))
        else:
            raise TypeError('Fault detail Must be dict, got', type(inst.detail))

        return self.gen_members_parent(ctx, cls, inst, parent, tag_name, subelts)

    def schema_validation_error_to_parent(self, ctx, cls, inst, parent, ns):
        tag_name = "{%s}Fault" % self.ns_soap_env

        subelts = [
            E("{%s}Reason" % self.soap_env, inst.faultstring),
            E("{%s}Role" % self.soap_env, inst.faultactor),
        ]

        if isinstance(inst.faultcode, string_types):
            value, faultcodes  = self.gen_fault_codes(inst.faultcode)

            code = E("{%s}Code" % self.soap_env)
            code.append(E("{%s}Value" % self.soap_env, value))

            child_subcode = 0
            for value in faultcodes:
                if child_subcode:
                    child_subcode = self.generate_subcode(value, child_subcode)
                else:
                    child_subcode = self.generate_subcode(value)
            code.append(child_subcode)

            _append(subelts, code)

        _append(subelts, E('{%s}Detail' % self.soap_env, inst.detail))

        return self.gen_members_parent(ctx, cls, inst, parent, tag_name, subelts)

    def fault_from_element(self, ctx, cls, element):

        nsmap  = element.nsmap

        code = self.generate_faultcode(element)
        reason = element.find("soap:Reason/soap:Text", namespaces=nsmap).text.strip()
        role = element.find("soap:Role", namespaces=nsmap)
        node = element.find("soap:Node", namespaces=nsmap)
        detail = element.find("soap:Detail", namespaces=nsmap)
        faultactor = ''
        if role:
            faultactor += role.text.strip()
        if node:
            faultactor += node.text.strip()
        return cls(faultcode=code, faultstring=reason,
                   faultactor = faultactor, detail=detail)
<commit_msg>minor<commit_after>#
# spyne - Copyright (C) Spyne contributors.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
#

"""
The ``spyne.protoco.soap.soap12`` module contains the implementation of a
subset of the Soap 1.2 standard.

This modules is EXPERIMENTAL.
"""

import logging

from lxml.builder import E

from spyne.protocol.soap.soap11 import Soap11
from spyne.protocol.xml import _append
from spyne.util.six import string_types
from spyne.util.etreeconv import root_dict_to_etree
from spyne.const.xml_ns import soap12_env, const_prefmap

logger = logging.getLogger(__name__)
logger_invalid = logging.getLogger(__name__ + ".invalid")

class Soap12(Soap11):
    """
    The base implementation of a subset of the Soap 1.2 standard. The
    document is available here: http://www.w3.org/TR/soap12/
    """
    mime_type = 'application/soap+xml; charset=utf-8'

    soap_env = const_prefmap[soap12_env]
    ns_soap_env = soap12_env

    def generate_subcode(self, value, subcode=None):
        subcode_node = E("{%s}Subcode" % self.soap_env)
        subcode_node.append(E("{%s}Value" % self.soap_env, value))
        if subcode:
            subcode_node.append(subcode)
        return subcode_node

    def gen_fault_codes(self, faultstring):
        faultstrings = faultstring.split('.')
        value = faultstrings.pop(0)

        if value == 'Client':
            value = 'Sender'
        elif value == 'Server':
            value = 'Receiver'
        else:
            raise TypeError('Wrong fault code, got', type(faultstring))

        return value, faultstrings

    def generate_faultcode(self, element):
        nsmap = element.nsmap
        faultcode = []
        faultcode.append(element.find('soap:Code/soap:Value', namespaces=nsmap).text)
        subcode = element.find('soap:Code/soap:Subcode', namespaces=nsmap)
        while subcode:
            faultcode.append(subcode.find('soap:Value', namespaces=nsmap).text)
            subcode = subcode.find('soap:Subcode', namespaces=nsmap)

        return '.'.join(faultcode)

    def fault_to_parent(self, ctx, cls, inst, parent, ns, *args, **kwargs):
        tag_name = "{%s}Fault" % self.ns_soap_env

        subelts = [
            E("{%s}Reason" % self.soap_env, inst.faultstring),
            E("{%s}Role" % self.soap_env, inst.faultactor),
        ]

        if isinstance(inst.faultcode, string_types):
            value, faultcodes  = self.gen_fault_codes(inst.faultcode)

            code = E("{%s}Code" % self.soap_env)
            code.append(E("{%s}Value" % self.soap_env, value))

            child_subcode = 0
            for value in faultcodes:
                if child_subcode:
                    child_subcode = self.generate_subcode(value, child_subcode)
                else:
                    child_subcode = self.generate_subcode(value)
            code.append(child_subcode)

            _append(subelts, code)

        if isinstance(inst.detail, dict):
            _append(subelts, E('{%s}Detail' % self.soap_env, root_dict_to_etree(inst.detail)))

        elif inst.detail is None:
            raise TypeError('Fault detail Must be dict, got', type(inst.detail))
        else:
            raise TypeError('Fault detail Must be dict, got', type(inst.detail))

        return self.gen_members_parent(ctx, cls, inst, parent, tag_name, subelts)

    def schema_validation_error_to_parent(self, ctx, cls, inst, parent, ns):
        tag_name = "{%s}Fault" % self.ns_soap_env

        subelts = [
            E("{%s}Reason" % self.soap_env, inst.faultstring),
            E("{%s}Role" % self.soap_env, inst.faultactor),
        ]

        if isinstance(inst.faultcode, string_types):
            value, faultcodes  = self.gen_fault_codes(inst.faultcode)

            code = E("{%s}Code" % self.soap_env)
            code.append(E("{%s}Value" % self.soap_env, value))

            child_subcode = 0
            for value in faultcodes:
                if child_subcode:
                    child_subcode = self.generate_subcode(value, child_subcode)
                else:
                    child_subcode = self.generate_subcode(value)
            code.append(child_subcode)

            _append(subelts, code)

        _append(subelts, E('{%s}Detail' % self.soap_env, inst.detail))

        return self.gen_members_parent(ctx, cls, inst, parent, tag_name, subelts)

    def fault_from_element(self, ctx, cls, element):

        nsmap  = element.nsmap

        code = self.generate_faultcode(element)
        reason = element.find("soap:Reason/soap:Text", namespaces=nsmap).text.strip()
        role = element.find("soap:Role", namespaces=nsmap)
        node = element.find("soap:Node", namespaces=nsmap)
        detail = element.find("soap:Detail", namespaces=nsmap)
        faultactor = ''
        if role:
            faultactor += role.text.strip()
        if node:
            faultactor += node.text.strip()
        return cls(faultcode=code, faultstring=reason,
                   faultactor = faultactor, detail=detail)
<|endoftext|>


<commit_before>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os
import repository

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self, justContentsForConfig = 0):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    if justContentsForConfig:
			if l[-1] == "-":
			    # XXX just hope for the best?
			    pass
			else:
			    flags = int(l[-1], 16)
			    if flags & files._FILE_FLAG_CONFIG:
				assert(self.hasFileContents(l[1]))
		    else:
			assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	assert(not oldVersion or oldVersion.timeStamp)
	assert(newVersion.timeStamp)
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, csPkg):
	assert(not csPkg.getOldVersion() or csPkg.getOldVersion().timeStamp)
	assert(csPkg.getNewVersion().timeStamp)

	self.newPackages.append(csPkg)
	if csPkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	assert(version.timeStamp)
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash, contents):
	self.fileContents[hash] = contents

    def getFileContents(self, hash):
	return self.fileContents[hash].get()

    def hasFileContents(self, hash):
	return self.fileContents.has_key(hash)

    def getFileList(self):
	return self.files.items()

    def remapPaths(self, map):
	for pkgCs in self.newPackages:
	    pkgCs.remapPaths(map)

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages:
	    pkg.formatToFile(self, cfg, f)
	for (pkgName, version) in self.oldPackages:
	    print pkgName, "removed", version.asString(cfg.defaultbranch)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def getFileOldVersion(self, fileId):
	return self.files[fileId][0]

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s\n" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents.keys():
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    # if availableFiles is set, this includes the contents that it can
    # find, but doesn't worry about files which it can't find
    def invert(self, repos, availableFiles = 0):
	assert(not self.abstract)
	# this is easy to fix if it turns out to be necessary
	assert(not self.oldPackages)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    if not pkgCs.getOldVersion():
		# this was a new package, and the inverse of a new
		# package is an old package
		inversion.oldPackage(pkgCs.getName(), pkgCs.getNewVersion())
		continue

	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    # this is a modified package and needs to be inverted

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))

		if (not availableFiles) or \
			    repos.hasFileContents(origFile.sha1()):
		    inversion.addFileContents(origFile.sha1())

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    if (not availableFiles) or \
				repos.hasFileContents(origFile.sha1()):
			inversion.addFileContents(origFile.sha1())

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	self.newPackages = []
	self.oldPackages = []
	self.files = {}
	self.fileContents = {}
	self.abstract = 0

class ChangeSetFromRepository(ChangeSet):

    def newPackage(self, pkg):
	# add the time stamps to the package version numbers
	pkg.changeOldVersion(self.repos.getFullVersion(pkg.getName(),
						       pkg.getOldVersion()))
	pkg.changeNewVersion(self.repos.getFullVersion(pkg.getName(),
						       pkg.getNewVersion()))
	ChangeSet.newPackage(self, pkg)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def addFileContents(self, hash):
	return ChangeSet.addFileContents(hash,
			    repostitory.FileContentsFromChangeSet(cs, hash))

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, version)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file, justContentsForConfig = 0):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate(justContentsForConfig)

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSet()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFileContents(hash,
			  repository.FileContentsFromFilesystem(realPath))

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion, (not oldVersion)))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<commit_msg>fix for abstract change sets<commit_after>#
# Copyright (c) 2004 Specifix, Inc.
# All rights reserved
#

import filecontainer
import files
import package
import versions
import os
import repository

class ChangeSet:

    def isAbstract(self):
	return self.abstract

    def validate(self, justContentsForConfig = 0):
	for pkg in self.getNewPackageList():
	    # if this is abstract, we can't have any removed or changed files
	    if not pkg.getOldVersion():
		assert(not pkg.getChangedFileList())
		assert(not pkg.getOldFileList())

	    list = pkg.getNewFileList() + pkg.getChangedFileList()

	    # new and changed files need to have a file entry for the right
	    # version along with the contents for files which have any
	    for (fileId, path, version) in list:
		assert(self.files.has_key(fileId))
		(oldVersion, newVersion, info) = self.files[fileId]
		assert(newVersion.equal(version))

		l = info.split()
		if (l[0] == "src" or l[0] == "f") and l[1] != "-":
		    if justContentsForConfig:
			if l[-1] == "-":
			    # XXX just hope for the best?
			    pass
			else:
			    flags = int(l[-1], 16)
			    if flags & files._FILE_FLAG_CONFIG:
				assert(self.hasFileContents(l[1]))
		    else:
			assert(self.hasFileContents(l[1]))

	    # old files should not have any file entries
	    for fileId in pkg.getOldFileList():
		assert(not self.files.has_key(fileId))

    def getFileContents(self, fileId):
	raise NotImplementedError

    def hasFileContents(self, fileId):
	raise NotImplementedError

    def addFile(self, fileId, oldVersion, newVersion, csInfo):
	assert(not oldVersion or oldVersion.timeStamp)
	assert(newVersion.timeStamp)
	self.files[fileId] = (oldVersion, newVersion, csInfo)

    def newPackage(self, csPkg):
	assert(not csPkg.getOldVersion() or csPkg.getOldVersion().timeStamp)
	assert(csPkg.getNewVersion().timeStamp)

	self.newPackages.append(csPkg)
	if csPkg.isAbstract():
	    self.abstract = 1

    def oldPackage(self, name, version):
	assert(version.timeStamp)
	self.oldPackages.append((name, version))

    def getNewPackageList(self):
	return self.newPackages

    def getOldPackageList(self):
	return self.oldPackages

    def addFileContents(self, hash, contents):
	self.fileContents[hash] = contents

    def getFileContents(self, hash):
	return self.fileContents[hash].get()

    def hasFileContents(self, hash):
	return self.fileContents.has_key(hash)

    def getFileList(self):
	return self.files.items()

    def remapPaths(self, map):
	for pkgCs in self.newPackages:
	    pkgCs.remapPaths(map)

    def formatToFile(self, cfg, f):
	for pkg in self.newPackages:
	    pkg.formatToFile(self, cfg, f)
	for (pkgName, version) in self.oldPackages:
	    print pkgName, "removed", version.asString(cfg.defaultbranch)

    def getFileChange(self, fileId):
	return self.files[fileId][2]

    def getFileOldVersion(self, fileId):
	return self.files[fileId][0]

    def headerAsString(self):
	rc = ""
	for pkg in self.getNewPackageList():
            rc += pkg.freeze()

	for (pkgName, version) in self.getOldPackageList():
	    rc += "SRS PKG REMOVED %s %s\n" % (pkgName, version.freeze())

	for (fileId, (oldVersion, newVersion, csInfo)) in self.getFileList():
	    if oldVersion:
		oldStr = oldVersion.freeze()
	    else:
		oldStr = "(none)"

	    rc += "SRS FILE CHANGESET %s %s %s\n%s\n" % \
			    (fileId, oldStr, newVersion.freeze(), csInfo)

	return rc

    def writeToFile(self, outFileName):
	try:
	    outFile = open(outFileName, "w+")
	    csf = filecontainer.FileContainer(outFile)
	    outFile.close()

	    csf.addFile("SRSCHANGESET", self.headerAsString(), "")

	    for hash in self.fileContents.keys():
		f = self.getFileContents(hash)
		csf.addFile(hash, f, "")
		f.close()

	    csf.close()
	except:
	    os.unlink(outFileName)
	    raise

    # if availableFiles is set, this includes the contents that it can
    # find, but doesn't worry about files which it can't find
    def invert(self, repos, availableFiles = 0):
	assert(not self.abstract)
	# this is easy to fix if it turns out to be necessary
	assert(not self.oldPackages)

	inversion = ChangeSetFromRepository(repos)

	for pkgCs in self.getNewPackageList():
	    if not pkgCs.getOldVersion():
		# this was a new package, and the inverse of a new
		# package is an old package
		inversion.oldPackage(pkgCs.getName(), pkgCs.getNewVersion())
		continue

	    pkg = repos.getPackageVersion(pkgCs.getName(),
					  pkgCs.getOldVersion())

	    # this is a modified package and needs to be inverted

	    invertedPkg = package.PackageChangeSet(pkgCs.getName(),
			       pkgCs.getNewVersion(), pkgCs.getOldVersion())

	    for (fileId, path, version) in pkgCs.getNewFileList():
		invertedPkg.oldFile(fileId)

	    for fileId in pkgCs.getOldFileList():
		(path, version) = pkg.getFile(fileId)
		invertedPkg.newFile(fileId, path, version)

		origFile = repos.getFileVersion(fileId, version)
		inversion.addFile(fileId, None, version, origFile.diff(None))

		if (not availableFiles) or \
			    repos.hasFileContents(origFile.sha1()):
		    inversion.addFileContents(origFile.sha1())

	    for (fileId, newPath, newVersion) in pkgCs.getChangedFileList():
		(curPath, curVersion) = pkg.getFile(fileId)
		invertedPkg.changedFile(fileId, curPath, curVersion)

		(oldVersion, newVersion, csInfo) = self.files[fileId]
		assert(curVersion.equal(oldVersion))

		origFile = repos.getFileVersion(fileId, oldVersion)
		newFile = repos.getFileVersion(fileId, oldVersion)
		newFile.applyChange(csInfo)

		inversion.addFile(fileId, newVersion, oldVersion,
				  origFile.diff(newFile))

		if origFile.sha1() != newFile.sha1():
		    if (not availableFiles) or \
				repos.hasFileContents(origFile.sha1()):
			inversion.addFileContents(origFile.sha1())

	    inversion.newPackage(invertedPkg)

	return inversion

    def __init__(self):
	self.newPackages = []
	self.oldPackages = []
	self.files = {}
	self.fileContents = {}
	self.abstract = 0

class ChangeSetFromRepository(ChangeSet):

    def newPackage(self, pkg):
	# add the time stamps to the package version numbers
	if pkg.getOldVersion():
	    pkg.changeOldVersion(self.repos.getFullVersion(pkg.getName(),
							   pkg.getOldVersion()))
	pkg.changeNewVersion(self.repos.getFullVersion(pkg.getName(),
						       pkg.getNewVersion()))
	ChangeSet.newPackage(self, pkg)

    def __init__(self, repos):
	self.repos = repos
	ChangeSet.__init__(self)

class ChangeSetFromAbstractChangeSet(ChangeSet):

    def addFileContents(self, hash):
	return ChangeSet.addFileContents(hash,
			    repostitory.FileContentsFromChangeSet(cs, hash))

    def __init__(self, absCS):
	self.absCS = absCS
	ChangeSet.__init__(self)

class ChangeSetFromFile(ChangeSet):

    def getFileContents(self, hash):
	return self.csf.getFile(hash)

    def hasFileContents(self, hash):
	return self.csf.hasFile(hash)

    def read(self, file):
	f = open(file, "r")
	self.csf = filecontainer.FileContainer(f)
	f.close()

	control = self.csf.getFile("SRSCHANGESET")

	lines = control.readLines()
	i = 0
	while i < len(lines):
	    header = lines[i][:-1]
	    i = i + 1

	    if header.startswith("SRS PKG REMOVED "):
		(pkgName, verStr) = header.split()[3:6]
		version = versions.ThawVersion(verStr)
		self.oldPackage(pkgName, version)
	    elif header.startswith("SRS PKG "):
		l = header.split()

		pkgType = l[2]
		pkgName = l[3]

		if pkgType == "CHANGESET":
		    oldVersion = versions.ThawVersion(l[4])
		    rest = 5
		elif pkgType == "NEW" or pkgType == "ABSTRACT":
		    oldVersion = None
		    rest = 4
		else:
		    raise IOError, "invalid line in change set %s" % file

		newVersion = versions.ThawVersion(l[rest])
		lineCount = int(l[rest + 1])

		pkg = package.PackageChangeSet(pkgName, oldVersion, newVersion,
				       abstract = (pkgType == "ABSTRACT"))

		end = i + lineCount
		while i < end:
		    pkg.parse(lines[i][:-1])
		    i = i + 1

		self.newPackage(pkg)
	    elif header.startswith("SRS FILE CHANGESET "):
		(fileId, oldVerStr, newVerStr) = header.split()[3:6]
		if oldVerStr == "(none)":
		    oldVersion = None
		else:
		    oldVersion = versions.ThawVersion(oldVerStr)
		newVersion = versions.ThawVersion(newVerStr)
		self.addFile(fileId, oldVersion, newVersion, lines[i][:-1])
		i = i + 1
	    else:
		print header
		raise IOError, "invalid line in change set %s" % file

	    header = control.read()

    def __init__(self, file, justContentsForConfig = 0):
	ChangeSet.__init__(self)
	self.read(file)
	self.validate(justContentsForConfig)

# old may be None
def fileChangeSet(fileId, old, new):
    hash = None

    if old and old.__class__ == new.__class__:
	diff = new.diff(old)
	if isinstance(new, files.RegularFile) and      \
		  isinstance(old, files.RegularFile)   \
		  and new.sha1() != old.sha1():
	    hash = new.sha1()
    else:
	# different classes; these are always written as abstract changes
	old = None
	diff = new.infoLine()
	if isinstance(new, files.RegularFile):
	    hash = new.sha1()

    return (diff, hash)

# this creates an abstract changeset
#
# expects a list of (pkg, fileMap) tuples
#
def CreateFromFilesystem(pkgList):
    cs = ChangeSet()

    for (pkg, fileMap) in pkgList:
        version = pkg.getVersion()
	(pkgChgSet, filesNeeded) = pkg.diff(None, abstract = 1)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (file, realPath, filePath) = fileMap[fileId]
	    (filecs, hash) = fileChangeSet(fileId, None, file)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFileContents(hash,
			  repository.FileContentsFromFilesystem(realPath))

    return cs

# creates a change set from the version of a package installed in the
# database against the files installed on the local system
def CreateAgainstLocal(cfg, db, pkgList):
    cs = ChangeSetFromFilesystem()

    for pkgName in pkgList:
	allVersions = db.getPackageVersionList(pkgName)
	if not allVersions: continue

	assert(len(allVersions) == 1)
	dbPkg = db.getPackageVersion(pkgName, allVersions[0])
	localPkg = db.getPackageVersion(pkgName, allVersions[0])

	localVersion = allVersions[0].fork(versions.LocalBranch(),
					   sameVerRel = 1)
	localPkg.changeVersion(localVersion)

	changedFiles = {}
	for (fileId, path, version) in localPkg.fileList():
	    dbFile = db.getFileVersion(fileId, version)

	    if isinstance(dbFile, files.SourceFile):
		shortName = pkgName.split(':')[-2]
		srcPath = cfg.sourcepath % {'pkgname': shortName }
		realPath = cfg.root + srcPath + "/" + path
		localFile = files.FileFromFilesystem(realPath, fileId, "src")
	    else:
		realPath = cfg.root + path
		localFile = files.FileFromFilesystem(realPath, fileId)

	    localFile.flags(dbFile.flags())

	    if not dbFile.same(localFile):
		fileVersion = version.fork(versions.LocalBranch(),
					   sameVerRel = 1)
		localPkg.updateFile(fileId, path, fileVersion)
		changedFiles[fileId] = (dbFile, localFile, realPath)

	(pkgChgSet, filesNeeded) = localPkg.diff(dbPkg)
	cs.newPackage(pkgChgSet)

	for (fileId, oldVersion, newVersion, path) in filesNeeded:
	    (dbFile, localFile, fullPath)  = changedFiles[fileId]
	    (filecs, hash) = fileChangeSet(fileId, dbFile, localFile)
	    cs.addFile(fileId, oldVersion, newVersion, filecs)

	    if hash:
		cs.addFilePointer(hash, fullPath)

    return cs

def ChangeSetCommand(repos, cfg, packageName, outFileName, oldVersionStr, \
	      newVersionStr):
    if packageName[0] != ":":
	packageName = cfg.packagenamespace + ":" + packageName

    newVersion = versions.VersionFromString(newVersionStr, cfg.defaultbranch)

    if (oldVersionStr):
	oldVersion = versions.VersionFromString(oldVersionStr,
					        cfg.defaultbranch)
    else:
	oldVersion = None

    list = []
    for name in repos.getPackageList(packageName):
	list.append((name, oldVersion, newVersion, (not oldVersion)))

    cs = repos.createChangeSet(list)
    cs.writeToFile(outFileName)
<|endoftext|>


<commit_before># Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013 ETH Zurich.

import scheduling

import pdb
import logging

from pygraph.algorithms.minmax import shortest_path
from pygraph.classes.graph import graph
from pygraph.classes.digraph import digraph

SORT_SUBTREE=False

# --------------------------------------------------
class SortLongest(scheduling.Scheduling):
    """
    Naive scheduling.

    Send messages in the order encoded in the model.
    """
    def __search(self, n1, n2):
        """Sort function used to sort by edge cost

        Sort by the cost first, if the costs are identical, sort by
        the neighbor's node ID.

        """
        res = cmp(n1[0], n2[0])
        if res == 0:
            return -1*cmp(n1[1], n2[1])
        else:
            return -1*res

    def find_schedule(self, sending_node, active_nodes=None):
        """Find a schedule for the given node.

        @param sending_node Sending node for which to determine scheduling
        @return List of neighbors in FIFO order.
        """
        if SORT_SUBTREE:
            nb=self.__get_longest_path(sending_node, [])
            nb.sort(self.__search)
        else:
            nb=self.__sort_edge_weight(sending_node, active_nodes)
            nb.sort(self.__search, reverse=True)
        return nb

    def __sort_edge_weight(self, src, active_nodes):
        """Sort the nodes by edge weight, rather than the cost of the entire
        subtree.

        """
        out = []
        omit = active_nodes if active_nodes != None else []
        for nb in self.graph.neighbors(src):
            if not nb in omit:
                cost = self.graph.edge_weight((src, nb))
                out.append((cost, nb))
        return out

    def __get_longest_path_for_node(self, node, omit, depth):
        """Return the length of the longest path starting at the given node
        Note: Graph must not have cycles (is this true?)
        """
        assert not omit == None
        l = 0
        for n in self.graph.neighbors(node):   ## node -> n
            if not n in omit:
                o_ = omit
                o_.append(node)
                c1 = self.graph.edge_weight((node, n))
                c2 = self.__get_longest_path_for_node(n, o_, depth+1)
                ltmp = c1 + c2
                indent = ''
                for i in range(depth):
                    indent += ' '
                logging.info("%sPath length from %s via %s is %d (%d + %d)" %
                             (indent, node, n, ltmp, c1, c2))
                l = max(l, ltmp)
        return l

    def __get_longest_path(self, src, omit):
        """Return the maximum length for subtrees starting at every node
        given in nblist
        """
        paths = []

        assert not omit == None
        omit.append(src)

        assert src in self.graph.nodes()

        for n in self.graph.neighbors(src): ## src -> n
            if not n in omit:
                m = self.graph.edge_weight((src, n)) + \
                    self.__get_longest_path_for_node(n, omit, 0)
                paths.append((m, n))
        return paths
<commit_msg>Fix: sort_longest: longest first, not shortest<commit_after># Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013 ETH Zurich.

import scheduling

import pdb
import logging

from pygraph.algorithms.minmax import shortest_path
from pygraph.classes.graph import graph
from pygraph.classes.digraph import digraph

SORT_SUBTREE=False

# --------------------------------------------------
class SortLongest(scheduling.Scheduling):
    """
    Naive scheduling.

    Send messages in the order encoded in the model.
    """
    def __search(self, n1, n2):
        """Sort function used to sort by edge cost

        Sort by the cost first, if the costs are identical, sort by
        the neighbor's node ID.

        """
        res = cmp(n1[0], n2[0])
        if res == 0:
            return -1*cmp(n1[1], n2[1])
        else:
            return -1*res

    def find_schedule(self, sending_node, active_nodes=None):
        """Find a schedule for the given node.

        @param sending_node Sending node for which to determine scheduling
        @return List of neighbors in FIFO order.
        """
        if SORT_SUBTREE:
            nb=self.__get_longest_path(sending_node, [])
            nb.sort(self.__search)
        else:
            nb=self.__sort_edge_weight(sending_node, active_nodes)
            nb.sort(self.__search)
        return nb

    def __sort_edge_weight(self, src, active_nodes):
        """Sort the nodes by edge weight, rather than the cost of the entire
        subtree.

        """
        out = []
        omit = active_nodes if active_nodes != None else []
        for nb in self.graph.neighbors(src):
            if not nb in omit:
                cost = self.graph.edge_weight((src, nb))
                out.append((cost, nb))
        return out

    def __get_longest_path_for_node(self, node, omit, depth):
        """Return the length of the longest path starting at the given node
        Note: Graph must not have cycles (is this true?)
        """
        assert not omit == None
        l = 0
        for n in self.graph.neighbors(node):   ## node -> n
            if not n in omit:
                o_ = omit
                o_.append(node)
                c1 = self.graph.edge_weight((node, n))
                c2 = self.__get_longest_path_for_node(n, o_, depth+1)
                ltmp = c1 + c2
                indent = ''
                for i in range(depth):
                    indent += ' '
                logging.info("%sPath length from %s via %s is %d (%d + %d)" %
                             (indent, node, n, ltmp, c1, c2))
                l = max(l, ltmp)
        return l

    def __get_longest_path(self, src, omit):
        """Return the maximum length for subtrees starting at every node
        given in nblist
        """
        paths = []

        assert not omit == None
        omit.append(src)

        assert src in self.graph.nodes()

        for n in self.graph.neighbors(src): ## src -> n
            if not n in omit:
                m = self.graph.edge_weight((src, n)) + \
                    self.__get_longest_path_for_node(n, omit, 0)
                paths.append((m, n))
        return paths
<|endoftext|>