* improve UI concept a bit
- re-introduce the somewhat redundant base class UI (it used to exist!) - improve and factor cli specific stuff into pisi.cli - introduce ack and choose functions, implement choose in cli - get rid of the cli-specific slash-ns, so many of them :/ * work towards doing the list type in autoxml the right way - breaks the unit test, but cleaner than before now - after lists, classes and localized text - that's how i'll implement the component tree, hmmm :) * some more detail on the todo
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
PISI - Packages Installed Succesfully as Intented
|
||||
|
||||
PISI is a new package manager implemented in python.
|
||||
PISI is a new package manager implemented in python for the PARDUS
|
||||
distribution.
|
||||
|
||||
Main features
|
||||
|
||||
- Implemented in python.
|
||||
- All specification and intermediate data is in an XML format.
|
||||
- Fast database access implemented with berkeley DB.
|
||||
- Integrates both low-level and high-level package operations.
|
||||
|
||||
|
||||
@@ -77,29 +77,39 @@ Legend:
|
||||
/ multiple package repository (eray)
|
||||
+ decide how to implement
|
||||
+ support repo order
|
||||
- support medium types
|
||||
? support medium types
|
||||
- internet: http/ftp
|
||||
- local repository
|
||||
- cdrom dvd
|
||||
/ UI
|
||||
- improve interface
|
||||
- ask alternatives to choose from (eray)
|
||||
- pyqt UI class to interface
|
||||
? pyqt UI class to interface
|
||||
- PISIMAT
|
||||
/ pykde GUI (cartman)
|
||||
+ improve interface
|
||||
+ refactor UI, reintroduce base class
|
||||
+ add an ack interface, start cleaning up a bit
|
||||
+ cli
|
||||
+ ask alternatives to choose from (eray)
|
||||
+ eliminate \n's from infos, what's the point?
|
||||
+ metaclass coolness for CLI command framework (eray)
|
||||
/ i18n support (those who know how to do it)
|
||||
/ autoxml: automated xml processing
|
||||
+ design
|
||||
+ basic types
|
||||
/ list typ
|
||||
- class typ
|
||||
/ localtext type
|
||||
/ components
|
||||
+ requirements
|
||||
- xml format (baris) (bunlarin class'leri yazilmali)
|
||||
- query components
|
||||
- xml format (bunlarin class'leri yazilmali)
|
||||
- query components: list of components/packages in a component
|
||||
- install/remove components
|
||||
/ i18n support
|
||||
- configure-pending (eray)
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
+ upgrade (eray)
|
||||
+ upgrade operation
|
||||
+ test upgrade op
|
||||
+ system-wide upgrade (upgrade-all) komutu
|
||||
/ overhaul installdb (try to merge its use with packagedb) (eray)
|
||||
- configure-pending (eray)
|
||||
? ordered configure install thing (eray)
|
||||
- partial caching and automatic resume for file download (meren)
|
||||
- use a separate "partial/" subdir like urpmi
|
||||
@@ -117,11 +127,11 @@ Legend:
|
||||
+ MetaData
|
||||
+ Files
|
||||
- Index (o kadar onemli degil)
|
||||
- implement missing unit tests
|
||||
+ implement missing unit tests
|
||||
+ sourcedb
|
||||
+ package (baris)
|
||||
+ configuration file (baris)
|
||||
- non-interactive use (baris)
|
||||
? non-interactive use (baris)
|
||||
+ incremental build (eray)
|
||||
+ generate binary release number by comparing MD5s (eray)
|
||||
+ keep track of successfully completed configure, make, install
|
||||
@@ -133,6 +143,10 @@ Legend:
|
||||
|
||||
5. Post Release
|
||||
|
||||
- a database of components: faster access
|
||||
- more support for categories:
|
||||
- put categories into a database
|
||||
- fast query for categories
|
||||
/ make a package and sourcedb for each repo
|
||||
+ packagedb
|
||||
- sourceb
|
||||
|
||||
@@ -25,7 +25,7 @@ def handle_exception(exception, value, tb):
|
||||
import pisi.ui
|
||||
from pisi.xmlext import XmlError
|
||||
|
||||
ui = pisi.ui.CLI()
|
||||
ui = pisi.cli.CLI()
|
||||
|
||||
if exception == exceptions.KeyboardInterrupt:
|
||||
ui.error(_("\nKeyboardInterrupt: Exiting...\n"))
|
||||
|
||||
@@ -25,7 +25,7 @@ class ConfigureError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
ctx.ui.error(Exception)
|
||||
if can_access_file('config.log'):
|
||||
ctx.ui.error('\n!!! Please attach the config.log to your bug report:\n%s/config.log\n' % os.getcwd())
|
||||
ctx.ui.error('\n!!! Please attach the config.log to your bug report:\n%s/config.log' % os.getcwd())
|
||||
|
||||
class MakeError(pisi.actionsapi.Error):
|
||||
def __init__(self, Exception):
|
||||
|
||||
@@ -35,16 +35,16 @@ def preplib_so(sourceDirectory):
|
||||
pass
|
||||
|
||||
def gnuconfig_update():
|
||||
''' copy newest config.* onto source's '''
|
||||
''' copy newest config.* onto source\'s '''
|
||||
for root, dirs, files in os.walk(os.getcwd()):
|
||||
for file in files:
|
||||
if file in ['config.sub', 'config.guess']:
|
||||
copy('/usr/share/gnuconfig/%s' % file, os.path.join(root, file))
|
||||
ctx.ui.info('GNU Config Update Finished...\n')
|
||||
ctx.ui.info('GNU Config Update Finished.')
|
||||
|
||||
def libtoolize(parameters = ''):
|
||||
if system('/usr/bin/libtoolize %s' % parameters):
|
||||
raise RunTimeError('!!! Running libtoolize failed...')
|
||||
raise RunTimeError('Running libtoolize failed...')
|
||||
|
||||
def gen_usr_ldscript(dynamicLib):
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ def doman(*sourceFiles):
|
||||
pageName, pageDirectory = source[:source.rindex('.')], \
|
||||
source[source.rindex('.')+1:]
|
||||
except ValueError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [doman]: Wrong man page file...\n')
|
||||
ctx.ui.error('\n!!! ActionsAPI [doman]: Wrong man page file...')
|
||||
|
||||
makedirs(manDIR + '/man%s' % pageDirectory)
|
||||
system('install -m0644 %s %s' % (source, manDIR + '/man%s' % pageDirectory))
|
||||
@@ -185,7 +185,7 @@ def dosym(sourceFile, destinationFile):
|
||||
try:
|
||||
os.symlink(sourceFile, get.installDIR() + destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...\n')
|
||||
ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...')
|
||||
|
||||
def insinto(destinationDirectory, sourceFile, destinationFile = ''):
|
||||
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
|
||||
|
||||
@@ -43,7 +43,7 @@ def echo(destionationFile, content):
|
||||
f.write("%s\n" % content)
|
||||
f.close()
|
||||
except IOError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [echo]: Can\'t append to file...\n')
|
||||
ctx.ui.error(' ActionsAPI [echo]: Can\'t append to file...')
|
||||
|
||||
def chmod(sourceFile, mode = 0755):
|
||||
'''change the mode of sourceFile to the mode'''
|
||||
@@ -52,9 +52,9 @@ def chmod(sourceFile, mode = 0755):
|
||||
try:
|
||||
os.chmod(file, mode)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [chmod]: Operation not permitted...\n')
|
||||
ctx.ui.error(' ActionsAPI [chmod]: Operation not permitted...')
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [chmod]: File doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [chmod]: File doesn\'t exists...')
|
||||
|
||||
def chown(sourceFile, uid = 0, gid = 0):
|
||||
'''change the owner and group id of sourceFile to the numeric uid and gid'''
|
||||
@@ -62,16 +62,16 @@ def chown(sourceFile, uid = 0, gid = 0):
|
||||
try:
|
||||
os.chown(sourceFile, uid, gid)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [chown]: Operation not permitted...\n')
|
||||
ctx.ui.error(' ActionsAPI [chown]: Operation not permitted...')
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [chown]: File doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [chown]: File doesn\'t exists...')
|
||||
|
||||
def sym(sourceFile, destinationFile):
|
||||
'''creates symbolic link'''
|
||||
try:
|
||||
os.symlink(sourceFile, destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [sym]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [sym]: Permission denied...')
|
||||
|
||||
def unlink(sourceFile):
|
||||
'''remove the file path'''
|
||||
@@ -79,11 +79,11 @@ def unlink(sourceFile):
|
||||
try:
|
||||
os.unlink(sourceFile)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [unlink]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [unlink]: Permission denied.')
|
||||
elif isDirectory(sourceFile):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [unlink]: File doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [unlink]: File doesn\'t exists.')
|
||||
|
||||
def unlinkDir(sourceDirectory):
|
||||
'''delete an entire directory tree'''
|
||||
@@ -91,11 +91,11 @@ def unlinkDir(sourceDirectory):
|
||||
try:
|
||||
shutil.rmtree(sourceDirectory)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [unlinkDir]: Operation not permitted...\n')
|
||||
ctx.ui.error(' ActionsAPI [unlinkDir]: Operation not permitted.')
|
||||
elif isFile(sourceDirectory):
|
||||
pass
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [unlinkDir]: Directory doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [unlinkDir]: Directory doesn\'t exists.')
|
||||
|
||||
def move(sourceFile, destinationFile):
|
||||
'''recursively move a sourceFile or directory to destinationFile'''
|
||||
@@ -104,9 +104,9 @@ def move(sourceFile, destinationFile):
|
||||
try:
|
||||
shutil.move(file, destinationFile)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [move]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [move]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [move]: File doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [move]: File doesn\'t exists.')
|
||||
|
||||
def copy(sourceFile, destinationFile):
|
||||
'''recursively copy a sourceFile or directory to destinationFile'''
|
||||
@@ -115,9 +115,9 @@ def copy(sourceFile, destinationFile):
|
||||
try:
|
||||
shutil.copy(file, destinationFile)
|
||||
except IOError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [copy]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [copy]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [copy]: File doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [copy]: File doesn\'t exists.')
|
||||
|
||||
def copytree(source, destination, sym = False):
|
||||
'''recursively copy an entire directory tree rooted at source'''
|
||||
@@ -125,9 +125,9 @@ def copytree(source, destination, sym = False):
|
||||
try:
|
||||
shutil.copytree(source, destination, sym)
|
||||
except OSError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [copytree]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [copytree]: Permission denied.')
|
||||
else:
|
||||
ctx.ui.error('\n!!! ActionsAPI [copytree]: Directory doesn\'t exists...\n')
|
||||
ctx.ui.error(' ActionsAPI [copytree]: Directory doesn\'t exists.')
|
||||
|
||||
def touch(sourceFile):
|
||||
'''changes the access time of the 'sourceFile', or creates it if it is not exist'''
|
||||
@@ -139,7 +139,7 @@ def touch(sourceFile):
|
||||
f = open(sourceFile, 'w')
|
||||
f.close()
|
||||
except IOError:
|
||||
ctx.ui.error('\n!!! ActionsAPI [touch]: Permission denied...\n')
|
||||
ctx.ui.error(' ActionsAPI [touch]: Permission denied.')
|
||||
|
||||
def cd(directoryName = ''):
|
||||
'''change directory'''
|
||||
@@ -187,12 +187,12 @@ def dirName(sourceFile):
|
||||
def system(command):
|
||||
#FIXME: String formatting
|
||||
command = command.replace(" ", " ")
|
||||
ctx.ui.debug('executing %s\n' % command)
|
||||
ctx.ui.debug('executing %s' % command)
|
||||
p = os.popen(command)
|
||||
while 1:
|
||||
line = p.readline()
|
||||
if not line:
|
||||
break
|
||||
ctx.ui.debug(line)
|
||||
ctx.ui.debug(line[0:len(line)-1])
|
||||
|
||||
return p.close()
|
||||
|
||||
+29
-28
@@ -7,9 +7,10 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
|
||||
# top level PISI interfaces
|
||||
# a facade to the entire PISI system
|
||||
"""Top level PISI interfaces. a facade to the entire PISI system"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -29,11 +30,13 @@ import pisi.packagedb as packagedb
|
||||
import pisi.repodb
|
||||
import pisi.installdb
|
||||
from pisi.index import Index
|
||||
import pisi.cli
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
def init(database = True, options = None, ui = None ):
|
||||
"""Initialize PiSi subsystem"""
|
||||
|
||||
@@ -41,11 +44,10 @@ def init(database = True, options = None, ui = None ):
|
||||
ctx.config = pisi.config.Config(options)
|
||||
|
||||
if ui is None:
|
||||
import pisi.ui
|
||||
if options:
|
||||
pisi.context.ui = pisi.ui.CLI(options.debug)
|
||||
pisi.context.ui = pisi.cli.CLI(options.debug)
|
||||
else:
|
||||
pisi.context.ui = pisi.ui.CLI()
|
||||
pisi.context.ui = pisi.cli.CLI()
|
||||
else:
|
||||
pisi.context.ui = ui
|
||||
|
||||
@@ -58,7 +60,6 @@ def init(database = True, options = None, ui = None ):
|
||||
# import pisi.sourcedb
|
||||
# pisi.sourcedb.init()
|
||||
|
||||
|
||||
def install(packages):
|
||||
"""install a list of packages (either files/urls, or names)"""
|
||||
|
||||
@@ -73,23 +74,23 @@ def install(packages):
|
||||
else:
|
||||
return install_pkg_names(packages)
|
||||
|
||||
#FIXME: As Gurer warns, something's fishy with this exception proc.
|
||||
except InstallError, e:
|
||||
ctx.ui.error("%s\n" % e)
|
||||
ctx.ui.error("InstallError:%s" % e)
|
||||
|
||||
except packagedb.Error, e:
|
||||
ctx.ui.error("PackageDBError: (%s)\n" % e)
|
||||
ctx.ui.error("Package is not installable.\n")
|
||||
ctx.ui.error("PackageDBError: (%s)" % e)
|
||||
ctx.ui.error("Package is not installable.")
|
||||
|
||||
#except Exception, e:
|
||||
# print e
|
||||
# ctx.ui.error("Error: %s\n" % e)
|
||||
|
||||
# ctx.ui.error("Error: %s" % e)
|
||||
|
||||
def install_pkg_files(package_URIs):
|
||||
"""install a number of pisi package files"""
|
||||
from package import Package
|
||||
|
||||
ctx.ui.debug('A = %s\n' % str(package_URIs))
|
||||
ctx.ui.debug('A = %s' % str(package_URIs))
|
||||
|
||||
for x in package_URIs:
|
||||
if not x.endswith(ctx.const.package_prefix):
|
||||
@@ -142,7 +143,7 @@ def install_pkg_files(package_URIs):
|
||||
A = d_t.keys()
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to install.\n')
|
||||
ctx.ui.info('No packages to install.')
|
||||
return True
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
@@ -186,10 +187,10 @@ def install_pkg_names(A):
|
||||
the repository, trying to perform a minimum number of
|
||||
installs"""
|
||||
|
||||
ctx.ui.debug('A = %s\n' % str(A))
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to install.\n')
|
||||
ctx.ui.info('No packages to install.')
|
||||
return True
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
@@ -241,7 +242,7 @@ def upgrade_pkg_names(A):
|
||||
Ap = []
|
||||
for x in A:
|
||||
if not ctx.installdb.is_installed(x):
|
||||
ctx.ui.info('Package %s is not installed.\n' % x)
|
||||
ctx.ui.info('Package %s is not installed.' % x)
|
||||
continue
|
||||
(version, release, build) = ctx.installdb.get_version(x)
|
||||
pkg = packagedb.get_package(x)
|
||||
@@ -253,15 +254,15 @@ def upgrade_pkg_names(A):
|
||||
else:
|
||||
#ctx.ui.info('Package %s cannot be upgraded. ' % x)
|
||||
ctx.ui.info('Package %s is already at its latest version %s,\
|
||||
release %s, build %s.\n'
|
||||
release %s, build %s.'
|
||||
% (x, pkg.version, pkg.release, pkg.build))
|
||||
A = Ap
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to upgrade.\n')
|
||||
ctx.ui.info('No packages to upgrade.')
|
||||
return True
|
||||
|
||||
ctx.ui.debug('A = %s\n' % str(A))
|
||||
ctx.ui.debug('A = %s' % str(A))
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
# install / reinstall
|
||||
@@ -337,11 +338,11 @@ def remove(A):
|
||||
if ctx.installdb.is_installed(x):
|
||||
Ap.append(x)
|
||||
else:
|
||||
ctx.ui.info('Package %s does not exist. Cannot remove.\n' % x)
|
||||
ctx.ui.info('Package %s does not exist. Cannot remove.' % x)
|
||||
A = Ap
|
||||
|
||||
if len(A)==0:
|
||||
ctx.ui.info('No packages to remove.\n')
|
||||
ctx.ui.info('No packages to remove.')
|
||||
return True
|
||||
|
||||
# try to construct a pisi graph of packages to
|
||||
@@ -377,7 +378,7 @@ def remove(A):
|
||||
if ctx.installdb.is_installed(x):
|
||||
operations.remove_single(x)
|
||||
else:
|
||||
ctx.ui.info('Package %s is not installed. Cannot remove.\n' % x)
|
||||
ctx.ui.info('Package %s is not installed. Cannot remove.' % x)
|
||||
|
||||
return True # everything went OK :)
|
||||
|
||||
@@ -423,11 +424,11 @@ def info_name(package_name):
|
||||
|
||||
def index(repo_dir = '.'):
|
||||
|
||||
ctx.ui.info('* Building index of PISI files under %s\n' % repo_dir)
|
||||
ctx.ui.info('* Building index of PISI files under %s' % repo_dir)
|
||||
index = Index()
|
||||
index.index(repo_dir)
|
||||
index.write(ctx.const.pisi_index)
|
||||
ctx.ui.info('* Index file written\n')
|
||||
ctx.ui.info('* Index file written')
|
||||
|
||||
|
||||
def add_repo(name, indexuri):
|
||||
@@ -438,16 +439,16 @@ def remove_repo(name):
|
||||
if ctx.repodb.has_repo(name):
|
||||
ctx.repodb.remove_repo(name)
|
||||
else:
|
||||
ctx.ui.error('* Repository %s does not exist. Cannot remove.\n'
|
||||
ctx.ui.error('* Repository %s does not exist. Cannot remove.'
|
||||
% name)
|
||||
|
||||
def update_repo(repo):
|
||||
|
||||
ctx.ui.info('* Updating repository: %s\n' % repo)
|
||||
ctx.ui.info('* Updating repository: %s' % repo)
|
||||
index = Index()
|
||||
index.read(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo)
|
||||
index.update_db(repo)
|
||||
ctx.ui.info('* Package database updated.\n')
|
||||
ctx.ui.info('* Package database updated.')
|
||||
|
||||
|
||||
# build functions...
|
||||
@@ -513,7 +514,7 @@ def build_until(pspecfile, state, authInfo=None):
|
||||
pb.compile_action_script()
|
||||
|
||||
last = pb.get_state()
|
||||
ctx.ui.info("Last state was %s\n"%last)
|
||||
ctx.ui.info("Last state was %s"%last)
|
||||
|
||||
if not last: last = "none"
|
||||
|
||||
|
||||
+11
-11
@@ -71,7 +71,7 @@ def check_path_collision(package, pkgList):
|
||||
# path.pathname: /usr/share/doc
|
||||
if util.subpath(pinfo.pathname, path.pathname):
|
||||
collisions.append(path.pathname)
|
||||
ctx.ui.error(_('Path %s belongs in multiple packages\n') %
|
||||
ctx.ui.error(_('Path %s belongs in multiple packages') %
|
||||
path.pathname)
|
||||
return collisions
|
||||
|
||||
@@ -141,7 +141,7 @@ class PisiBuild:
|
||||
def build(self):
|
||||
"""Build the package in one shot."""
|
||||
|
||||
ctx.ui.info(_("Building PISI source package: %s\n") % self.spec.source.name)
|
||||
ctx.ui.info(_("Building PISI source package: %s") % self.spec.source.name)
|
||||
util.xterm_title(_("Building PISI source package: %s\n") % self.spec.source.name)
|
||||
|
||||
self.compile_action_script()
|
||||
@@ -178,15 +178,15 @@ class PisiBuild:
|
||||
os.environ.update(evn)
|
||||
|
||||
def fetch_source_archive(self):
|
||||
ctx.ui.info(_("Fetching source from: %s\n") % self.spec.source.archiveUri)
|
||||
ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archiveUri)
|
||||
self.sourceArchive.fetch()
|
||||
ctx.ui.info(_("Source archive is stored: %s/%s\n")
|
||||
ctx.ui.info(_("Source archive is stored: %s/%s")
|
||||
%(ctx.config.archives_dir(), self.spec.source.archiveName))
|
||||
|
||||
def unpack_source_archive(self):
|
||||
ctx.ui.info(_("Unpacking archive..."))
|
||||
self.sourceArchive.unpack()
|
||||
ctx.ui.info(_(" unpacked (%s)\n") % self.bctx.pkg_work_dir())
|
||||
ctx.ui.info(_(" unpacked (%s)") % self.bctx.pkg_work_dir())
|
||||
self.set_state("unpack")
|
||||
|
||||
def run_setup_action(self):
|
||||
@@ -220,10 +220,10 @@ class PisiBuild:
|
||||
buf = open(scriptfile).read()
|
||||
exec compile(buf, "error", "exec") in localSymbols, globalSymbols
|
||||
except IOError, e:
|
||||
ctx.ui.error(_("Unable to read Action Script (%s): %s\n") %(scriptfile,e))
|
||||
ctx.ui.error(_("Unable to read Action Script (%s): %s") %(scriptfile,e))
|
||||
sys.exit(1)
|
||||
except SyntaxError, e:
|
||||
ctx.ui.error (_("SyntaxError in Action Script (%s): %s\n") %(scriptfile,e))
|
||||
ctx.ui.error (_("SyntaxError in Action Script (%s): %s") %(scriptfile,e))
|
||||
sys.exit(1)
|
||||
|
||||
self.actionLocals = localSymbols
|
||||
@@ -427,17 +427,17 @@ class PisiBuild:
|
||||
self.spec.source.version,
|
||||
self.spec.source.release)
|
||||
|
||||
ctx.ui.action(_("** Building package %s\n") % package.name);
|
||||
ctx.ui.action(_("** Building package %s") % package.name);
|
||||
|
||||
ctx.ui.action(_("Generating %s...") % ctx.const.files_xml)
|
||||
self.gen_files_xml(package)
|
||||
ctx.ui.info(_(" done.\n"))
|
||||
ctx.ui.info(_(" done."))
|
||||
|
||||
ctx.ui.action(_("Generating %s...") % ctx.const.metadata_xml)
|
||||
self.gen_metadata_xml(package)
|
||||
ctx.ui.info(_(" done.\n"))
|
||||
ctx.ui.info(_(" done."))
|
||||
|
||||
ctx.ui.action(_("Creating PISI package %s\n") % name)
|
||||
ctx.ui.action(_("Creating PISI package %s") % name)
|
||||
|
||||
pkg = Package(name, 'w')
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
Writing commands:
|
||||
-----------------
|
||||
|
||||
TODO: this is a *bit* out of date now.
|
||||
|
||||
Subclass from Command.
|
||||
|
||||
Be careful not to import PiSi modules before Command is run, e.g.
|
||||
|
||||
@@ -11,10 +11,74 @@
|
||||
# pisi.cli package version
|
||||
__version__ = "0.1"
|
||||
|
||||
import sys
|
||||
|
||||
import pisi
|
||||
from pisi.ui import UI
|
||||
from pisi.cli.colors import colorize
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
class Exception(pisi.Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CLI(UI):
|
||||
"Command Line Interface"
|
||||
|
||||
def __init__(self, show_debug = False, show_verbose = False):
|
||||
super(CLI, self).__init__(show_debug, show_verbose)
|
||||
|
||||
def output(self, str):
|
||||
sys.stdout.write(str)
|
||||
sys.stdout.flush()
|
||||
|
||||
def info(self, msg, verbose = False):
|
||||
# TODO: need to look at more kinds of info messages
|
||||
# let's cheat from KDE :)
|
||||
if verbose and self.show_verbose:
|
||||
self.output(msg + '\n')
|
||||
elif not verbose:
|
||||
self.output(msg + '\n')
|
||||
|
||||
def warning(self,msg):
|
||||
self.output(colorize('Warning:' + msg + '\n', 'purple'))
|
||||
|
||||
def error(self,msg):
|
||||
self.output(colorize('Error:' + msg + '\n', 'red'))
|
||||
|
||||
def action(self,msg):
|
||||
#TODO: this seems quite redundant?
|
||||
self.output(colorize(msg + '\n', 'green'))
|
||||
|
||||
def choose(self, msg, opts):
|
||||
print msg
|
||||
for i in range(0,len(opts)):
|
||||
print i + 1, opts(i)
|
||||
while True:
|
||||
s = raw_input(msg + colorize('1-%d' % len(opts), 'red'))
|
||||
try:
|
||||
opt = int(s)
|
||||
if 1 <= opt and opt <= len(opts):
|
||||
return opts(opt-1)
|
||||
except (Exception,e):
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
if ctx.config.options and ctx.config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
s = raw_input(msg + colorize('(yes/no)', 'red'))
|
||||
if s.startswith('y') or s.startswith('Y'):
|
||||
return True
|
||||
if s.startswith('n') or s.startswith('N'):
|
||||
return False
|
||||
|
||||
def display_progress(self, pd):
|
||||
out = '\r%-30.30s %3d%% %12.2f %s' % \
|
||||
(pd['filename'], pd['percent'], pd['rate'], pd['symbol'])
|
||||
self.output(out)
|
||||
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ dependency spec"""
|
||||
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
|
||||
for dep in deps:
|
||||
if not sat(dep):
|
||||
ctx.ui.error('Package %s does not satisfy dependency %s\n' %
|
||||
ctx.ui.error('Package %s does not satisfy dependency %s' %
|
||||
(pkg,dep))
|
||||
return False
|
||||
return True
|
||||
@@ -124,7 +124,7 @@ def installable(pkg):
|
||||
"""calculate if pkg is installable currently
|
||||
which means it has to satisfy both install and runtime dependencies"""
|
||||
if not packagedb.has_package(pkg):
|
||||
ctx.ui.info("Package " + pkg + " is not present in the package database\n");
|
||||
ctx.ui.info("Package " + pkg + " is not present in the package database");
|
||||
return False
|
||||
elif satisfies_runtime_deps(pkg):
|
||||
return True
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ def fetch_url(url, dest, progress=None):
|
||||
fetch.progress = progress
|
||||
fetch.fetch()
|
||||
if progress:
|
||||
ctx.ui.info('\n')
|
||||
pass
|
||||
#ctx.ui.info('\n')
|
||||
|
||||
|
||||
class Fetcher:
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ class Index(XmlFile):
|
||||
for root, dirs, files in os.walk(repo_uri):
|
||||
for fn in files:
|
||||
if fn.endswith(ctx.const.package_prefix):
|
||||
ctx.ui.info('Adding ' + fn + ' to package index\n')
|
||||
ctx.ui.info('Adding ' + fn + ' to package index')
|
||||
self.add_package(os.path.join(root, fn), repo_uri)
|
||||
|
||||
def update_db(self, repo):
|
||||
@@ -93,6 +93,6 @@ class Index(XmlFile):
|
||||
md.package.packageURI = util.removepathprefix(repo_uri, path)
|
||||
# check package semantics
|
||||
if md.has_errors():
|
||||
ctx.ui.error('Package ' + md.package.name + ': metadata corrupt\n')
|
||||
ctx.ui.error('Package ' + md.package.name + ': metadata corrupt')
|
||||
else:
|
||||
self.packages.append(md.package)
|
||||
|
||||
+9
-9
@@ -44,7 +44,7 @@ class Installer:
|
||||
|
||||
def install(self, ask_reinstall = True):
|
||||
"entry point"
|
||||
ctx.ui.info('Installing %s, version %s, release %s, build %s\n' %
|
||||
ctx.ui.info('Installing %s, version %s, release %s, build %s' %
|
||||
(self.pkginfo.name, self.pkginfo.version,
|
||||
self.pkginfo.release, self.pkginfo.build))
|
||||
self.ask_reinstall = ask_reinstall
|
||||
@@ -77,7 +77,7 @@ class Installer:
|
||||
# check dependencies
|
||||
if not dependency.installable(self.pkginfo.name):
|
||||
ctx.ui.error('Dependencies for ' + self.pkginfo.name +
|
||||
' not satisfied\n')
|
||||
' not satisfied')
|
||||
raise InstallError("Package not installable")
|
||||
|
||||
def reinstall(self):
|
||||
@@ -108,14 +108,14 @@ class Installer:
|
||||
# is this an upgrade?
|
||||
# determine and report the kind of upgrade: version, release, build
|
||||
if pkg.version > iversion:
|
||||
ctx.ui.info('Upgrading to new upstream version\n')
|
||||
ctx.ui.info('Upgrading to new upstream version')
|
||||
upgrade = True
|
||||
elif pkg.release > irelease:
|
||||
ctx.ui.info('Upgrading to new distribution release\n')
|
||||
ctx.ui.info('Upgrading to new distribution release')
|
||||
upgrade = True
|
||||
elif ((not ignore_build) and ibuild and pkg.build
|
||||
and pkg.build > ibuild):
|
||||
ctx.ui.info('Upgrading to new distribution build\n')
|
||||
ctx.ui.info('Upgrading to new distribution build')
|
||||
upgrade = True
|
||||
|
||||
# is this a downgrade? confirm this action.
|
||||
@@ -135,7 +135,7 @@ class Installer:
|
||||
def extract_install(self):
|
||||
"unzip package in place"
|
||||
|
||||
ctx.ui.info('Extracting files,\n')
|
||||
ctx.ui.info('Extracting files,')
|
||||
self.package.extract_dir_flat('install', ctx.config.destdir)
|
||||
|
||||
def store_pisi_files(self):
|
||||
@@ -145,14 +145,14 @@ class Installer:
|
||||
ctx.ui.info('Storing %s, ' % ctx.const.files_xml)
|
||||
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir())
|
||||
|
||||
ctx.ui.info('%s.\n' % ctx.const.metadata_xml)
|
||||
ctx.ui.info('%s.' % ctx.const.metadata_xml)
|
||||
self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
|
||||
# comar prefix is added to the pkg_dir while extracting comar
|
||||
# script file. so we'll use pkg_dir as destination.
|
||||
ctx.ui.info('Storing %s\n' % fpath)
|
||||
ctx.ui.info('Storing %s' % fpath)
|
||||
self.package.extract_file(fpath, self.package.pkg_dir())
|
||||
|
||||
def register_comar_scripts(self):
|
||||
@@ -160,7 +160,7 @@ class Installer:
|
||||
|
||||
for pcomar in self.metadata.package.providesComar:
|
||||
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script)
|
||||
ctx.ui.info("Registering COMAR script %s\n" % pcomar.script)
|
||||
ctx.ui.info("Registering COMAR script %s" % pcomar.script)
|
||||
# FIXME: We must check the result of the command (possibly
|
||||
# with id?)
|
||||
if comard:
|
||||
|
||||
@@ -51,7 +51,7 @@ class LockedDBShelf(shelve.DBShelf):
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError:
|
||||
import sys
|
||||
pisi.context.ui.error("Another instance of PISI is running. Try later!\n")
|
||||
pisi.context.ui.error("Another instance of PISI is running. Try later!")
|
||||
sys.exit(1)
|
||||
return self.db.open(filename, dbname, filetype, flags, mode)
|
||||
|
||||
|
||||
+4
-4
@@ -7,8 +7,9 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
#
|
||||
|
||||
# Package Operations: install/remove/upgrade
|
||||
"Package Operations: install/remove/upgrade"
|
||||
|
||||
import os
|
||||
|
||||
@@ -46,7 +47,6 @@ def remove_single(package_name):
|
||||
if comard:
|
||||
# FIXME: (return value)...
|
||||
comard.remove(package_name)
|
||||
ctx.ui.info('.\n')
|
||||
|
||||
def install_single(pkg, upgrade = False):
|
||||
"""install a single package from URI or ID"""
|
||||
@@ -81,12 +81,12 @@ def install_single_name(name, upgrade = False):
|
||||
pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
|
||||
str(pkg_uri.path()))
|
||||
|
||||
ctx.ui.debug("Package URI: %s\n" % pkg_path)
|
||||
ctx.ui.debug("Package URI: %s" % pkg_path)
|
||||
|
||||
# Package will handle remote file for us!
|
||||
install_single_file(pkg_path, upgrade)
|
||||
else:
|
||||
ctx.ui.error("Package %s not found in any active repository.\n" % pkg)
|
||||
ctx.ui.error("Package %s not found in any active repository." % pkg)
|
||||
|
||||
# deneme, don't remove ulan
|
||||
class AtomicOperation(object):
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ class PackageDB(object):
|
||||
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except IOError, e:
|
||||
import sys
|
||||
ctx.ui.error("PackageDB: %s\n" % e)
|
||||
ctx.ui.error("PackageDB: %s" % e)
|
||||
sys.exit(1)
|
||||
self.d = shelve.open(self.fname)
|
||||
self.dr = shelve.open(self.fname2)
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
# Authors: Baris Metin <baris@uludag.org.tr>
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
# python standard library
|
||||
|
||||
# Authors: Baris Metin <baris@uludag.org.tr
|
||||
# Eray Ozkural <eray@uludag.org.tr>
|
||||
|
||||
from os.path import join
|
||||
from os import access, R_OK
|
||||
|
||||
@@ -55,7 +54,7 @@ class SourceArchive:
|
||||
# check hash
|
||||
if util.check_file_hash(self.archiveFile, self.archiveSHA1):
|
||||
if interactive:
|
||||
ctx.ui.info('%s [cached]\n' % self.archiveName)
|
||||
ctx.ui.info('%s [cached]' % self.archiveName)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -74,7 +74,7 @@ class SourceFetcher(object):
|
||||
def fetch(self, appendDest=""):
|
||||
from fetcher import fetch_url
|
||||
|
||||
ctx.ui.info("Fetching %s\n" % self.url.uri)
|
||||
ctx.ui.info("Fetching %s" % self.url.uri)
|
||||
dest = join(self.dest, appendDest)
|
||||
fetch_url(self.url, dest)
|
||||
|
||||
|
||||
+12
-6
@@ -9,13 +9,19 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
|
||||
# files are specification files for PISI source packages. This module
|
||||
# provides read and write access to PSPEC files.
|
||||
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr
|
||||
# Gurer Ozen <gurer@uludag.org.tr>
|
||||
# Baris Metin <baris@uludag.org.tr>
|
||||
# History:
|
||||
# Baris wrote the first version, then Baris and Eray did
|
||||
# several revisions of it. It was rewritten in accordance
|
||||
# with Gurer's observations.
|
||||
|
||||
|
||||
"""Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC)
|
||||
files are specification files for PISI source packages. This module
|
||||
provides read and write access to PSPEC files."""
|
||||
|
||||
|
||||
# standard python modules
|
||||
from os.path import basename
|
||||
|
||||
+50
-54
@@ -7,63 +7,18 @@
|
||||
#
|
||||
# Please read the COPYING file.
|
||||
#
|
||||
|
||||
# generic user interface
|
||||
|
||||
#
|
||||
# Authors: Eray Ozkural <eray@uludag.org.tr>
|
||||
# Murat Eren <meren@uludag.org.tr>
|
||||
|
||||
import sys
|
||||
|
||||
from pisi.colors import colorize
|
||||
import pisi
|
||||
import pisi.context as ctx
|
||||
|
||||
|
||||
class CLI:
|
||||
"Command Line Interface"
|
||||
def __init__(self, debuggy = False, verbose = False):
|
||||
self.show_debug = debuggy
|
||||
self.show_verbose = verbose
|
||||
|
||||
def set_verbose(self, flag):
|
||||
self.show_verbose = flag
|
||||
|
||||
def set_debug(self, flag):
|
||||
self.show_debug = flag
|
||||
|
||||
def info(self, msg, verbose = False):
|
||||
if verbose and self.show_verbose:
|
||||
sys.stdout.write(colorize(msg, 'blue'))
|
||||
elif not verbose:
|
||||
sys.stdout.write(colorize(msg, 'blue'))
|
||||
sys.stdout.flush()
|
||||
|
||||
def debug(self, msg):
|
||||
if self.show_debug:
|
||||
sys.stdout.write(msg)
|
||||
sys.stdout.flush()
|
||||
|
||||
def warning(self,msg):
|
||||
sys.stdout.write(colorize('Warning:' + msg, 'purple'))
|
||||
sys.stdout.flush()
|
||||
|
||||
def error(self,msg):
|
||||
sys.stdout.write(colorize('Error:' + msg, 'red'))
|
||||
sys.stdout.flush()
|
||||
|
||||
def action(self,msg):
|
||||
sys.stdout.write(colorize(msg, 'green'))
|
||||
sys.stdout.flush()
|
||||
|
||||
def confirm(self, msg):
|
||||
from pisi.config import config
|
||||
if config.options and config.options.yes_all:
|
||||
return True
|
||||
while True:
|
||||
s = raw_input(msg + colorize('(yes/no)', 'red'))
|
||||
if s.startswith('y') or s.startswith('Y'):
|
||||
return True
|
||||
if s.startswith('n') or s.startswith('N'):
|
||||
return False
|
||||
class UI(object):
|
||||
"Abstract class for UI operations, derive from this."
|
||||
|
||||
class Progress:
|
||||
def __init__(self, totalsize):
|
||||
@@ -81,8 +36,49 @@ class CLI:
|
||||
else:
|
||||
return 0
|
||||
|
||||
def display_progress(self, pd):
|
||||
out = '\r%-30.30s %3d%% %12.2f %s' % \
|
||||
(pd['filename'], pd['percent'], pd['rate'], pd['symbol'])
|
||||
self.info(out)
|
||||
def __init__(self, debuggy = False, verbose = False):
|
||||
self.show_debug = debuggy
|
||||
self.show_verbose = verbose
|
||||
|
||||
def set_verbose(self, flag):
|
||||
self.show_verbose = flag
|
||||
|
||||
def set_debug(self, flag):
|
||||
self.show_debug = flag
|
||||
|
||||
def info(self, msg, verbose = False):
|
||||
"give an informative message"
|
||||
pass
|
||||
|
||||
def ack(self, msg):
|
||||
"inform the user of an important event and wait for acknowledgement"
|
||||
pass
|
||||
|
||||
def debug(self, msg):
|
||||
"show debugging info"
|
||||
if self.show_debug:
|
||||
self.info('DEBUG: ' + msg)
|
||||
|
||||
def warning(self,msg):
|
||||
"warn the user"
|
||||
pass
|
||||
|
||||
def error(self,msg):
|
||||
"inform a (possibly fatal) error"
|
||||
pass
|
||||
|
||||
def action(self,msg):
|
||||
"uh?"
|
||||
pass
|
||||
|
||||
def choose(self, msg, list):
|
||||
"ask the user to choose from a list of alternatives"
|
||||
pass
|
||||
|
||||
def confirm(self, msg):
|
||||
"ask a yes/no question"
|
||||
pass
|
||||
|
||||
def display_progress(self, pd):
|
||||
"display progress"
|
||||
pass
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ def clean_locks(top = '.'):
|
||||
for fn in files:
|
||||
if fn.endswith('.lock'):
|
||||
path = os.path.join(root, fn)
|
||||
ctx.ui.info('Removing lock %s\n', path)
|
||||
ctx.ui.info('Removing lock %s', path)
|
||||
os.unlink(path)
|
||||
|
||||
########################################
|
||||
|
||||
+125
-54
@@ -24,10 +24,11 @@
|
||||
in xml.dom :( )
|
||||
|
||||
autoxml is a metaclass for automatic XML translation, using
|
||||
a miniature type system. (w00t!)
|
||||
a miniature type system. (w00t!) This is based on an excellent
|
||||
high-level XML processing prototype that Gurer prepared.
|
||||
|
||||
Method names are mixedCase for compatibility with minidom,
|
||||
an old library.
|
||||
an old library.
|
||||
"""
|
||||
|
||||
# System
|
||||
@@ -38,22 +39,23 @@ import codecs
|
||||
# PiSi
|
||||
import pisi
|
||||
from pisi.xmlext import *
|
||||
import pisi.context as ctx
|
||||
|
||||
|
||||
class Error(pisi.Error):
|
||||
pass
|
||||
|
||||
|
||||
import types
|
||||
|
||||
mandatory, optional = range(2) # poor man's enum
|
||||
|
||||
# basic types
|
||||
|
||||
Text = types.StringType
|
||||
Integer = types.IntType
|
||||
|
||||
class LocalText:
|
||||
"handle tags with localized text"
|
||||
class LocalText(object):
|
||||
"""Handles XML tags/attributes with localized text"""
|
||||
|
||||
def __init__():
|
||||
locs = {}
|
||||
@@ -80,17 +82,89 @@ class LocalText:
|
||||
if not locs.has_key(L):
|
||||
L = 'en'
|
||||
if not locs.has_key(L):
|
||||
errs.append("Tag '%s' should have an English version\n" % d[2])
|
||||
#errs.append("Tag '%s' should have an English version\n" % d[2])
|
||||
return ""
|
||||
return locs[L]
|
||||
|
||||
|
||||
class autoxml(type):
|
||||
"high-level automatic XML transformation interface for xmlfile"
|
||||
"""High-level automatic XML transformation interface for xmlfile.
|
||||
The idea is to declare a class for each XML tag. Inside the
|
||||
class the tags and attributes nested in the tag are further
|
||||
elaborated. A simple example follows:
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
|
||||
This class defines a tag and an attribute nested in Employee
|
||||
class. Name is a string and type is an integer, called basic
|
||||
types.
|
||||
While the tag is mandatory, the attribute may be left out.
|
||||
|
||||
Other basic types supported are: xmlfile.Float, xmlfile.Double
|
||||
and (not implemented yet): xmlfile.Binary
|
||||
|
||||
By default, the class name is taken as the corresponding tag,
|
||||
which may be overridden by defining a tag attribute. Thus,
|
||||
the same tag may also be written as:
|
||||
|
||||
class EmployeeXML:
|
||||
...
|
||||
tag = 'Employee'
|
||||
...
|
||||
|
||||
In addition to basic types, we allow for two kinds of complex
|
||||
types: class types and list types.
|
||||
|
||||
A declared class can be nested in another class as follows
|
||||
|
||||
class Position:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
t_Description = [xmlfile.Text, xmlfile.optional]
|
||||
|
||||
which we can add to our Employee class.
|
||||
|
||||
class Employee:
|
||||
__metaclass__ = autoxml
|
||||
t_Name = [xmlfile.Text, xmlfile.mandatory]
|
||||
a_Type = [xmlfile.Integer, xmlfile.optional]
|
||||
t_Position = [Position, xmlfile.mandatory]
|
||||
|
||||
Note some unfortunate redundancy here with Position; this is
|
||||
justified by the implementation (kidding). Still, you might
|
||||
want to assign a different name than the class name that
|
||||
goes in there, which may be fully qualified.
|
||||
|
||||
There is more! Suppose we want to define a company, with
|
||||
of course many employees.
|
||||
|
||||
class Company:
|
||||
__metaclass__ = autoxml
|
||||
t_Employees = [ Employee, xmlfile.mandatory]
|
||||
|
||||
Logically, inside the Company tag, we will have several Employee
|
||||
tags, which are inserted to the Employees instance variable of
|
||||
Company in order of appearance.
|
||||
The mandatory flag here asserts that at least one such record
|
||||
is to be found.
|
||||
|
||||
It is also possible to change the XML path we expect the tag in,
|
||||
just like with any other tag.
|
||||
|
||||
t_Employees = [ Employee, xmlfile.mandatory, 'Employees/Employee']
|
||||
|
||||
|
||||
You see, it works like magic, when it works of course. All of it
|
||||
done without a single brain exploding.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, dict):
|
||||
|
||||
# add XmlFile as one of the superclasses
|
||||
# add XmlFile as one of the superclasses, we're smart
|
||||
bases = list(bases)
|
||||
if not XmlFile in bases:
|
||||
bases.append(XmlFile)
|
||||
@@ -98,7 +172,7 @@ class autoxml(type):
|
||||
# standard initialization
|
||||
super(autoxml, cls).__init__(name, bases, dict)
|
||||
|
||||
# initialize class attribute __xml_tags
|
||||
#TODO: initialize class attribute __xml_tags
|
||||
#setattr(cls, 'xml_variables', [])
|
||||
|
||||
if not dict.has_key('tag'):
|
||||
@@ -119,12 +193,15 @@ class autoxml(type):
|
||||
x = autoxml.gen_tag(cls, name)
|
||||
elif var.startswith('a_'):
|
||||
x = autoxml.gen_attr(cls, name)
|
||||
ctx.ui.debug(x)
|
||||
(init, decoder, encoder, formatter) = x
|
||||
inits.append(init)
|
||||
decoders.append(decoder)
|
||||
encoders.append(encoder)
|
||||
formatters.append(formatter)
|
||||
|
||||
varname = cls.mixed_case(name)
|
||||
|
||||
setattr(cls, 'initializers', inits)
|
||||
def initialize(self):
|
||||
for init in self.__class__.initializers:
|
||||
@@ -134,13 +211,13 @@ class autoxml(type):
|
||||
setattr(cls, 'decoders', decoders)
|
||||
def decode(self, node):
|
||||
for decoder in self.__class__.decoders:
|
||||
decoder(self, node)
|
||||
setattr(self, varname, decoder(self, node))
|
||||
setattr(cls, 'decode', decode)
|
||||
|
||||
setattr(cls, 'encoders', encoders)
|
||||
def encode(self, xml):
|
||||
for encoder in self.__class__.encoders:
|
||||
encoder(self, xml)
|
||||
encoder(self, xml, getattr(self, varname))
|
||||
setattr(cls, 'encode', encode)
|
||||
|
||||
setattr(cls, 'formatters', formatters)
|
||||
@@ -183,7 +260,9 @@ class autoxml(type):
|
||||
return identifier_p
|
||||
|
||||
def gen_basic(cls, token, val, readtext, writetext):
|
||||
"generate a basic tag or attribute"
|
||||
"""Generate a basic tag or attribute. This has got
|
||||
to be pretty generic so we can invoke it from the complex
|
||||
types such as Class and List"""
|
||||
name = cls.mixed_case(token)
|
||||
token_type = val[0]
|
||||
req = val[1]
|
||||
@@ -198,25 +277,23 @@ class autoxml(type):
|
||||
try:
|
||||
value = autoxml.basic_cons_map[token_type](text)
|
||||
except Error:
|
||||
return ['Type mismatch']
|
||||
raise Error('Type mismatch')
|
||||
setattr(self, name, value)
|
||||
else:
|
||||
if req == mandatory:
|
||||
return ['Mandatory argument not available']
|
||||
raise Error('Mandatory argument not available')
|
||||
else:
|
||||
setattr(self, name, None)
|
||||
return None
|
||||
|
||||
def encode(self, xml):
|
||||
def encode(self, xml, value):
|
||||
node = xml.newNode(cls.tag)
|
||||
if hasattr(self, name):
|
||||
writetext(xml, node, token, str(getattr(self, name)))
|
||||
writetext(xml, node, token, str(value))
|
||||
else:
|
||||
if req == mandatory:
|
||||
return ['Mandatory argument not available']
|
||||
raise Error('Mandatory argument not available')
|
||||
|
||||
def format(self):
|
||||
#print 'format:', name
|
||||
if hasattr(self, name):
|
||||
return '%s: %s\n' % (token, str(getattr(self, name)))
|
||||
else:
|
||||
@@ -226,69 +303,63 @@ class autoxml(type):
|
||||
|
||||
return initialize, decode, encode, format
|
||||
|
||||
def gen_basic_attr(cls, attr, val):
|
||||
def gen_basic_attr(cls, attr, spec):
|
||||
"""generate an attribute with a basic datatype"""
|
||||
def readtext(node, attr):
|
||||
return getNodeAttribute(node, attr)
|
||||
def writetext(xml, node, attr, value):
|
||||
node.setAttribute(attr, value)
|
||||
return autoxml.gen_basic(cls, attr, val, readtext, writetext)
|
||||
return autoxml.gen_basic(cls, attr, spec, readtext, writetext)
|
||||
|
||||
def gen_basic_tag(cls, tag, val):
|
||||
def gen_basic_tag(cls, tag, spec):
|
||||
"""generate a tag with a basic datatype"""
|
||||
def readtext(node, tag):
|
||||
return getNodeText(getNode(node, tag))
|
||||
def writetext(xml, node, tag, value):
|
||||
xml.addTextNodeUnder(node, tag, value)
|
||||
return autoxml.gen_basic(cls, tag, val, readtext, writetext)
|
||||
return autoxml.gen_basic(cls, tag, spec, readtext, writetext)
|
||||
|
||||
def gen_class_tag(cls, tag, val):
|
||||
pass
|
||||
|
||||
def gen_list_tag(cls, tag, val):
|
||||
name = cls.mixed_case(token)
|
||||
token_type = val[0]
|
||||
name = cls.mixed_case(tag)
|
||||
tag_type = val[0]
|
||||
req = val[1]
|
||||
|
||||
if len(val)>=3:
|
||||
path = val[2]
|
||||
path = val[2] # an alternative path specified
|
||||
else:
|
||||
path = tag
|
||||
if len(token_type!=1):
|
||||
raise Error('List type must contain one element')
|
||||
|
||||
x = autoxml.gen_tag_aux(cls, tag, tag_type, val)
|
||||
(x_init, x_decoder, x_encoder, x_formatter) = x
|
||||
path = tag # otherwise it's the same name as
|
||||
# the tag
|
||||
if len(tag_type) != 1:
|
||||
raise Error('List type must contain only one element')
|
||||
x = autoxml.gen_tag_aux(cls, tag, tag_type[0], val)
|
||||
(init_item, decode_item, encode_item, format_item) = x
|
||||
|
||||
def init(self):
|
||||
varname = cls.mixed_case(name)
|
||||
setattr(self, varname, [])
|
||||
setattr(self, name, [])
|
||||
|
||||
def decode(self, node):
|
||||
nodes = getAllNodes(node)
|
||||
if len(nodes) is 0 and req is mandatory:
|
||||
pass
|
||||
#FIXME: add error here
|
||||
for node in nodes:
|
||||
x_decoder(self, node)
|
||||
pass
|
||||
l = []
|
||||
self.nodes = getAllNodes(node, path)
|
||||
print 'F U', self.nodes
|
||||
if len(self.nodes) is 0 and req is mandatory:
|
||||
raise Error('Mandatory list empty')
|
||||
for node in self.nodes:
|
||||
l.append(decode_item(self, node))
|
||||
return l
|
||||
|
||||
def encode(self, xml):
|
||||
node = xml.newNode(cls.tag)
|
||||
if hasattr(self, name):
|
||||
writetext(xml, node, token, str(getattr(self, name)))
|
||||
else:
|
||||
if req == mandatory:
|
||||
return ['Mandatory argument not available']
|
||||
def encode(self, xml, value):
|
||||
pass
|
||||
|
||||
def format(self):
|
||||
#print 'format:', name
|
||||
if hasattr(self, name):
|
||||
return '%s: %s\n' % (token, str(getattr(self, name)))
|
||||
else:
|
||||
if req == mandatory:
|
||||
raise Error('Mandatory variable %s not available' % name)
|
||||
return ""
|
||||
s = ''
|
||||
l = getattr(self, name)
|
||||
for ix in range(len(l)):
|
||||
s += str(ix) + format_item(l[ix])
|
||||
return s
|
||||
|
||||
return (init, decode, encode, format)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user