* 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:
Eray Özkural
2005-09-19 01:30:36 +00:00
parent ec48344969
commit a18badd9f2
25 changed files with 380 additions and 217 deletions
+10 -1
View File
@@ -1,3 +1,12 @@
PISI - Packages Installed Succesfully as Intented 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.
+25 -11
View File
@@ -77,29 +77,39 @@ Legend:
/ multiple package repository (eray) / multiple package repository (eray)
+ decide how to implement + decide how to implement
+ support repo order + support repo order
- support medium types ? support medium types
- internet: http/ftp - internet: http/ftp
- local repository - local repository
- cdrom dvd - cdrom dvd
/ UI / UI
- improve interface ? pyqt UI class to interface
- ask alternatives to choose from (eray)
- pyqt UI class to interface
- PISIMAT - PISIMAT
/ pykde GUI (cartman) / 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) + 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 / components
+ requirements + requirements
- xml format (baris) (bunlarin class'leri yazilmali) - xml format (bunlarin class'leri yazilmali)
- query components - query components: list of components/packages in a component
- install/remove components - install/remove components
/ i18n support
- configure-pending (eray)
/ overhaul installdb (try to merge its use with packagedb) (eray)
+ upgrade (eray) + upgrade (eray)
+ upgrade operation + upgrade operation
+ test upgrade op + test upgrade op
+ system-wide upgrade (upgrade-all) komutu + system-wide upgrade (upgrade-all) komutu
/ overhaul installdb (try to merge its use with packagedb) (eray)
- configure-pending (eray)
? ordered configure install thing (eray) ? ordered configure install thing (eray)
- partial caching and automatic resume for file download (meren) - partial caching and automatic resume for file download (meren)
- use a separate "partial/" subdir like urpmi - use a separate "partial/" subdir like urpmi
@@ -117,11 +127,11 @@ Legend:
+ MetaData + MetaData
+ Files + Files
- Index (o kadar onemli degil) - Index (o kadar onemli degil)
- implement missing unit tests + implement missing unit tests
+ sourcedb + sourcedb
+ package (baris) + package (baris)
+ configuration file (baris) + configuration file (baris)
- non-interactive use (baris) ? non-interactive use (baris)
+ incremental build (eray) + incremental build (eray)
+ generate binary release number by comparing MD5s (eray) + generate binary release number by comparing MD5s (eray)
+ keep track of successfully completed configure, make, install + keep track of successfully completed configure, make, install
@@ -133,6 +143,10 @@ Legend:
5. Post Release 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 / make a package and sourcedb for each repo
+ packagedb + packagedb
- sourceb - sourceb
+1 -1
View File
@@ -25,7 +25,7 @@ def handle_exception(exception, value, tb):
import pisi.ui import pisi.ui
from pisi.xmlext import XmlError from pisi.xmlext import XmlError
ui = pisi.ui.CLI() ui = pisi.cli.CLI()
if exception == exceptions.KeyboardInterrupt: if exception == exceptions.KeyboardInterrupt:
ui.error(_("\nKeyboardInterrupt: Exiting...\n")) ui.error(_("\nKeyboardInterrupt: Exiting...\n"))
+1 -1
View File
@@ -25,7 +25,7 @@ class ConfigureError(pisi.actionsapi.Error):
def __init__(self, Exception): def __init__(self, Exception):
ctx.ui.error(Exception) ctx.ui.error(Exception)
if can_access_file('config.log'): 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): class MakeError(pisi.actionsapi.Error):
def __init__(self, Exception): def __init__(self, Exception):
+3 -3
View File
@@ -35,16 +35,16 @@ def preplib_so(sourceDirectory):
pass pass
def gnuconfig_update(): 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 root, dirs, files in os.walk(os.getcwd()):
for file in files: for file in files:
if file in ['config.sub', 'config.guess']: if file in ['config.sub', 'config.guess']:
copy('/usr/share/gnuconfig/%s' % file, os.path.join(root, file)) 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 = ''): def libtoolize(parameters = ''):
if system('/usr/bin/libtoolize %s' % parameters): if system('/usr/bin/libtoolize %s' % parameters):
raise RunTimeError('!!! Running libtoolize failed...') raise RunTimeError('Running libtoolize failed...')
def gen_usr_ldscript(dynamicLib): def gen_usr_ldscript(dynamicLib):
+2 -2
View File
@@ -124,7 +124,7 @@ def doman(*sourceFiles):
pageName, pageDirectory = source[:source.rindex('.')], \ pageName, pageDirectory = source[:source.rindex('.')], \
source[source.rindex('.')+1:] source[source.rindex('.')+1:]
except ValueError: 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) makedirs(manDIR + '/man%s' % pageDirectory)
system('install -m0644 %s %s' % (source, manDIR + '/man%s' % pageDirectory)) system('install -m0644 %s %s' % (source, manDIR + '/man%s' % pageDirectory))
@@ -185,7 +185,7 @@ def dosym(sourceFile, destinationFile):
try: try:
os.symlink(sourceFile, get.installDIR() + destinationFile) os.symlink(sourceFile, get.installDIR() + destinationFile)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...\n') ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...')
def insinto(destinationDirectory, sourceFile, destinationFile = ''): def insinto(destinationDirectory, sourceFile, destinationFile = ''):
'''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions''' '''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions'''
+19 -19
View File
@@ -43,7 +43,7 @@ def echo(destionationFile, content):
f.write("%s\n" % content) f.write("%s\n" % content)
f.close() f.close()
except IOError: 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): def chmod(sourceFile, mode = 0755):
'''change the mode of sourceFile to the mode''' '''change the mode of sourceFile to the mode'''
@@ -52,9 +52,9 @@ def chmod(sourceFile, mode = 0755):
try: try:
os.chmod(file, mode) os.chmod(file, mode)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [chmod]: Operation not permitted...\n') ctx.ui.error(' ActionsAPI [chmod]: Operation not permitted...')
else: 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): def chown(sourceFile, uid = 0, gid = 0):
'''change the owner and group id of sourceFile to the numeric uid and gid''' '''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: try:
os.chown(sourceFile, uid, gid) os.chown(sourceFile, uid, gid)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [chown]: Operation not permitted...\n') ctx.ui.error(' ActionsAPI [chown]: Operation not permitted...')
else: 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): def sym(sourceFile, destinationFile):
'''creates symbolic link''' '''creates symbolic link'''
try: try:
os.symlink(sourceFile, destinationFile) os.symlink(sourceFile, destinationFile)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [sym]: Permission denied...\n') ctx.ui.error(' ActionsAPI [sym]: Permission denied...')
def unlink(sourceFile): def unlink(sourceFile):
'''remove the file path''' '''remove the file path'''
@@ -79,11 +79,11 @@ def unlink(sourceFile):
try: try:
os.unlink(sourceFile) os.unlink(sourceFile)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [unlink]: Permission denied...\n') ctx.ui.error(' ActionsAPI [unlink]: Permission denied.')
elif isDirectory(sourceFile): elif isDirectory(sourceFile):
pass pass
else: else:
ctx.ui.error('\n!!! ActionsAPI [unlink]: File doesn\'t exists...\n') ctx.ui.error(' ActionsAPI [unlink]: File doesn\'t exists.')
def unlinkDir(sourceDirectory): def unlinkDir(sourceDirectory):
'''delete an entire directory tree''' '''delete an entire directory tree'''
@@ -91,11 +91,11 @@ def unlinkDir(sourceDirectory):
try: try:
shutil.rmtree(sourceDirectory) shutil.rmtree(sourceDirectory)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [unlinkDir]: Operation not permitted...\n') ctx.ui.error(' ActionsAPI [unlinkDir]: Operation not permitted.')
elif isFile(sourceDirectory): elif isFile(sourceDirectory):
pass pass
else: 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): def move(sourceFile, destinationFile):
'''recursively move a sourceFile or directory to destinationFile''' '''recursively move a sourceFile or directory to destinationFile'''
@@ -104,9 +104,9 @@ def move(sourceFile, destinationFile):
try: try:
shutil.move(file, destinationFile) shutil.move(file, destinationFile)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [move]: Permission denied...\n') ctx.ui.error(' ActionsAPI [move]: Permission denied.')
else: 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): def copy(sourceFile, destinationFile):
'''recursively copy a sourceFile or directory to destinationFile''' '''recursively copy a sourceFile or directory to destinationFile'''
@@ -115,9 +115,9 @@ def copy(sourceFile, destinationFile):
try: try:
shutil.copy(file, destinationFile) shutil.copy(file, destinationFile)
except IOError: except IOError:
ctx.ui.error('\n!!! ActionsAPI [copy]: Permission denied...\n') ctx.ui.error(' ActionsAPI [copy]: Permission denied.')
else: 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): def copytree(source, destination, sym = False):
'''recursively copy an entire directory tree rooted at source''' '''recursively copy an entire directory tree rooted at source'''
@@ -125,9 +125,9 @@ def copytree(source, destination, sym = False):
try: try:
shutil.copytree(source, destination, sym) shutil.copytree(source, destination, sym)
except OSError: except OSError:
ctx.ui.error('\n!!! ActionsAPI [copytree]: Permission denied...\n') ctx.ui.error(' ActionsAPI [copytree]: Permission denied.')
else: else:
ctx.ui.error('\n!!! ActionsAPI [copytree]: Directory doesn\'t exists...\n') ctx.ui.error(' ActionsAPI [copytree]: Directory doesn\'t exists.')
def touch(sourceFile): def touch(sourceFile):
'''changes the access time of the 'sourceFile', or creates it if it is not exist''' '''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 = open(sourceFile, 'w')
f.close() f.close()
except IOError: except IOError:
ctx.ui.error('\n!!! ActionsAPI [touch]: Permission denied...\n') ctx.ui.error(' ActionsAPI [touch]: Permission denied.')
def cd(directoryName = ''): def cd(directoryName = ''):
'''change directory''' '''change directory'''
@@ -187,12 +187,12 @@ def dirName(sourceFile):
def system(command): def system(command):
#FIXME: String formatting #FIXME: String formatting
command = command.replace(" ", " ") command = command.replace(" ", " ")
ctx.ui.debug('executing %s\n' % command) ctx.ui.debug('executing %s' % command)
p = os.popen(command) p = os.popen(command)
while 1: while 1:
line = p.readline() line = p.readline()
if not line: if not line:
break break
ctx.ui.debug(line) ctx.ui.debug(line[0:len(line)-1])
return p.close() return p.close()
+29 -28
View File
@@ -7,9 +7,10 @@
# #
# Please read the COPYING file. # Please read the COPYING file.
# #
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr>
# top level PISI interfaces """Top level PISI interfaces. a facade to the entire PISI system"""
# a facade to the entire PISI system
import os import os
import sys import sys
@@ -29,11 +30,13 @@ import pisi.packagedb as packagedb
import pisi.repodb import pisi.repodb
import pisi.installdb import pisi.installdb
from pisi.index import Index from pisi.index import Index
import pisi.cli
class Error(pisi.Error): class Error(pisi.Error):
pass pass
def init(database = True, options = None, ui = None ): def init(database = True, options = None, ui = None ):
"""Initialize PiSi subsystem""" """Initialize PiSi subsystem"""
@@ -41,11 +44,10 @@ def init(database = True, options = None, ui = None ):
ctx.config = pisi.config.Config(options) ctx.config = pisi.config.Config(options)
if ui is None: if ui is None:
import pisi.ui
if options: if options:
pisi.context.ui = pisi.ui.CLI(options.debug) pisi.context.ui = pisi.cli.CLI(options.debug)
else: else:
pisi.context.ui = pisi.ui.CLI() pisi.context.ui = pisi.cli.CLI()
else: else:
pisi.context.ui = ui pisi.context.ui = ui
@@ -58,7 +60,6 @@ def init(database = True, options = None, ui = None ):
# import pisi.sourcedb # import pisi.sourcedb
# pisi.sourcedb.init() # pisi.sourcedb.init()
def install(packages): def install(packages):
"""install a list of packages (either files/urls, or names)""" """install a list of packages (either files/urls, or names)"""
@@ -73,23 +74,23 @@ def install(packages):
else: else:
return install_pkg_names(packages) return install_pkg_names(packages)
#FIXME: As Gurer warns, something's fishy with this exception proc.
except InstallError, e: except InstallError, e:
ctx.ui.error("%s\n" % e) ctx.ui.error("InstallError:%s" % e)
except packagedb.Error, e: except packagedb.Error, e:
ctx.ui.error("PackageDBError: (%s)\n" % e) ctx.ui.error("PackageDBError: (%s)" % e)
ctx.ui.error("Package is not installable.\n") ctx.ui.error("Package is not installable.")
#except Exception, e: #except Exception, e:
# print e # print e
# ctx.ui.error("Error: %s\n" % e) # ctx.ui.error("Error: %s" % e)
def install_pkg_files(package_URIs): def install_pkg_files(package_URIs):
"""install a number of pisi package files""" """install a number of pisi package files"""
from package import Package 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: for x in package_URIs:
if not x.endswith(ctx.const.package_prefix): if not x.endswith(ctx.const.package_prefix):
@@ -142,7 +143,7 @@ def install_pkg_files(package_URIs):
A = d_t.keys() A = d_t.keys()
if len(A)==0: if len(A)==0:
ctx.ui.info('No packages to install.\n') ctx.ui.info('No packages to install.')
return True return True
# try to construct a pisi graph of packages to # 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 the repository, trying to perform a minimum number of
installs""" installs"""
ctx.ui.debug('A = %s\n' % str(A)) ctx.ui.debug('A = %s' % str(A))
if len(A)==0: if len(A)==0:
ctx.ui.info('No packages to install.\n') ctx.ui.info('No packages to install.')
return True return True
# try to construct a pisi graph of packages to # try to construct a pisi graph of packages to
@@ -241,7 +242,7 @@ def upgrade_pkg_names(A):
Ap = [] Ap = []
for x in A: for x in A:
if not ctx.installdb.is_installed(x): 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 continue
(version, release, build) = ctx.installdb.get_version(x) (version, release, build) = ctx.installdb.get_version(x)
pkg = packagedb.get_package(x) pkg = packagedb.get_package(x)
@@ -253,15 +254,15 @@ def upgrade_pkg_names(A):
else: else:
#ctx.ui.info('Package %s cannot be upgraded. ' % x) #ctx.ui.info('Package %s cannot be upgraded. ' % x)
ctx.ui.info('Package %s is already at its latest version %s,\ 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)) % (x, pkg.version, pkg.release, pkg.build))
A = Ap A = Ap
if len(A)==0: if len(A)==0:
ctx.ui.info('No packages to upgrade.\n') ctx.ui.info('No packages to upgrade.')
return True 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 # try to construct a pisi graph of packages to
# install / reinstall # install / reinstall
@@ -337,11 +338,11 @@ def remove(A):
if ctx.installdb.is_installed(x): if ctx.installdb.is_installed(x):
Ap.append(x) Ap.append(x)
else: 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 A = Ap
if len(A)==0: if len(A)==0:
ctx.ui.info('No packages to remove.\n') ctx.ui.info('No packages to remove.')
return True return True
# try to construct a pisi graph of packages to # try to construct a pisi graph of packages to
@@ -377,7 +378,7 @@ def remove(A):
if ctx.installdb.is_installed(x): if ctx.installdb.is_installed(x):
operations.remove_single(x) operations.remove_single(x)
else: 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 :) return True # everything went OK :)
@@ -423,11 +424,11 @@ def info_name(package_name):
def index(repo_dir = '.'): 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()
index.index(repo_dir) index.index(repo_dir)
index.write(ctx.const.pisi_index) index.write(ctx.const.pisi_index)
ctx.ui.info('* Index file written\n') ctx.ui.info('* Index file written')
def add_repo(name, indexuri): def add_repo(name, indexuri):
@@ -438,16 +439,16 @@ def remove_repo(name):
if ctx.repodb.has_repo(name): if ctx.repodb.has_repo(name):
ctx.repodb.remove_repo(name) ctx.repodb.remove_repo(name)
else: else:
ctx.ui.error('* Repository %s does not exist. Cannot remove.\n' ctx.ui.error('* Repository %s does not exist. Cannot remove.'
% name) % name)
def update_repo(repo): def update_repo(repo):
ctx.ui.info('* Updating repository: %s\n' % repo) ctx.ui.info('* Updating repository: %s' % repo)
index = Index() index = Index()
index.read(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo) index.read(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo)
index.update_db(repo) index.update_db(repo)
ctx.ui.info('* Package database updated.\n') ctx.ui.info('* Package database updated.')
# build functions... # build functions...
@@ -513,7 +514,7 @@ def build_until(pspecfile, state, authInfo=None):
pb.compile_action_script() pb.compile_action_script()
last = pb.get_state() 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" if not last: last = "none"
+11 -11
View File
@@ -71,7 +71,7 @@ def check_path_collision(package, pkgList):
# path.pathname: /usr/share/doc # path.pathname: /usr/share/doc
if util.subpath(pinfo.pathname, path.pathname): if util.subpath(pinfo.pathname, path.pathname):
collisions.append(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) path.pathname)
return collisions return collisions
@@ -141,7 +141,7 @@ class PisiBuild:
def build(self): def build(self):
"""Build the package in one shot.""" """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) util.xterm_title(_("Building PISI source package: %s\n") % self.spec.source.name)
self.compile_action_script() self.compile_action_script()
@@ -178,15 +178,15 @@ class PisiBuild:
os.environ.update(evn) os.environ.update(evn)
def fetch_source_archive(self): 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() 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)) %(ctx.config.archives_dir(), self.spec.source.archiveName))
def unpack_source_archive(self): def unpack_source_archive(self):
ctx.ui.info(_("Unpacking archive...")) ctx.ui.info(_("Unpacking archive..."))
self.sourceArchive.unpack() 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") self.set_state("unpack")
def run_setup_action(self): def run_setup_action(self):
@@ -220,10 +220,10 @@ class PisiBuild:
buf = open(scriptfile).read() buf = open(scriptfile).read()
exec compile(buf, "error", "exec") in localSymbols, globalSymbols exec compile(buf, "error", "exec") in localSymbols, globalSymbols
except IOError, e: 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) sys.exit(1)
except SyntaxError, e: 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) sys.exit(1)
self.actionLocals = localSymbols self.actionLocals = localSymbols
@@ -427,17 +427,17 @@ class PisiBuild:
self.spec.source.version, self.spec.source.version,
self.spec.source.release) 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) ctx.ui.action(_("Generating %s...") % ctx.const.files_xml)
self.gen_files_xml(package) self.gen_files_xml(package)
ctx.ui.info(_(" done.\n")) ctx.ui.info(_(" done."))
ctx.ui.action(_("Generating %s...") % ctx.const.metadata_xml) ctx.ui.action(_("Generating %s...") % ctx.const.metadata_xml)
self.gen_metadata_xml(package) 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') pkg = Package(name, 'w')
+2
View File
@@ -1,6 +1,8 @@
Writing commands: Writing commands:
----------------- -----------------
TODO: this is a *bit* out of date now.
Subclass from Command. Subclass from Command.
Be careful not to import PiSi modules before Command is run, e.g. Be careful not to import PiSi modules before Command is run, e.g.
+64
View File
@@ -11,10 +11,74 @@
# pisi.cli package version # pisi.cli package version
__version__ = "0.1" __version__ = "0.1"
import sys
import pisi import pisi
from pisi.ui import UI
from pisi.cli.colors import colorize
class Error(pisi.Error): class Error(pisi.Error):
pass pass
class Exception(pisi.Exception): class Exception(pisi.Exception):
pass 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
View File
@@ -111,7 +111,7 @@ dependency spec"""
def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep): def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep):
for dep in deps: for dep in deps:
if not sat(dep): 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)) (pkg,dep))
return False return False
return True return True
@@ -124,7 +124,7 @@ def installable(pkg):
"""calculate if pkg is installable currently """calculate if pkg is installable currently
which means it has to satisfy both install and runtime dependencies""" which means it has to satisfy both install and runtime dependencies"""
if not packagedb.has_package(pkg): 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 return False
elif satisfies_runtime_deps(pkg): elif satisfies_runtime_deps(pkg):
return True return True
+2 -1
View File
@@ -37,7 +37,8 @@ def fetch_url(url, dest, progress=None):
fetch.progress = progress fetch.progress = progress
fetch.fetch() fetch.fetch()
if progress: if progress:
ctx.ui.info('\n') pass
#ctx.ui.info('\n')
class Fetcher: class Fetcher:
+2 -2
View File
@@ -67,7 +67,7 @@ class Index(XmlFile):
for root, dirs, files in os.walk(repo_uri): for root, dirs, files in os.walk(repo_uri):
for fn in files: for fn in files:
if fn.endswith(ctx.const.package_prefix): 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) self.add_package(os.path.join(root, fn), repo_uri)
def update_db(self, repo): def update_db(self, repo):
@@ -93,6 +93,6 @@ class Index(XmlFile):
md.package.packageURI = util.removepathprefix(repo_uri, path) md.package.packageURI = util.removepathprefix(repo_uri, path)
# check package semantics # check package semantics
if md.has_errors(): if md.has_errors():
ctx.ui.error('Package ' + md.package.name + ': metadata corrupt\n') ctx.ui.error('Package ' + md.package.name + ': metadata corrupt')
else: else:
self.packages.append(md.package) self.packages.append(md.package)
+9 -9
View File
@@ -44,7 +44,7 @@ class Installer:
def install(self, ask_reinstall = True): def install(self, ask_reinstall = True):
"entry point" "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.name, self.pkginfo.version,
self.pkginfo.release, self.pkginfo.build)) self.pkginfo.release, self.pkginfo.build))
self.ask_reinstall = ask_reinstall self.ask_reinstall = ask_reinstall
@@ -77,7 +77,7 @@ class Installer:
# check dependencies # check dependencies
if not dependency.installable(self.pkginfo.name): if not dependency.installable(self.pkginfo.name):
ctx.ui.error('Dependencies for ' + self.pkginfo.name + ctx.ui.error('Dependencies for ' + self.pkginfo.name +
' not satisfied\n') ' not satisfied')
raise InstallError("Package not installable") raise InstallError("Package not installable")
def reinstall(self): def reinstall(self):
@@ -108,14 +108,14 @@ class Installer:
# is this an upgrade? # is this an upgrade?
# determine and report the kind of upgrade: version, release, build # determine and report the kind of upgrade: version, release, build
if pkg.version > iversion: if pkg.version > iversion:
ctx.ui.info('Upgrading to new upstream version\n') ctx.ui.info('Upgrading to new upstream version')
upgrade = True upgrade = True
elif pkg.release > irelease: elif pkg.release > irelease:
ctx.ui.info('Upgrading to new distribution release\n') ctx.ui.info('Upgrading to new distribution release')
upgrade = True upgrade = True
elif ((not ignore_build) and ibuild and pkg.build elif ((not ignore_build) and ibuild and pkg.build
and pkg.build > ibuild): and pkg.build > ibuild):
ctx.ui.info('Upgrading to new distribution build\n') ctx.ui.info('Upgrading to new distribution build')
upgrade = True upgrade = True
# is this a downgrade? confirm this action. # is this a downgrade? confirm this action.
@@ -135,7 +135,7 @@ class Installer:
def extract_install(self): def extract_install(self):
"unzip package in place" "unzip package in place"
ctx.ui.info('Extracting files,\n') ctx.ui.info('Extracting files,')
self.package.extract_dir_flat('install', ctx.config.destdir) self.package.extract_dir_flat('install', ctx.config.destdir)
def store_pisi_files(self): def store_pisi_files(self):
@@ -145,14 +145,14 @@ class Installer:
ctx.ui.info('Storing %s, ' % ctx.const.files_xml) ctx.ui.info('Storing %s, ' % ctx.const.files_xml)
self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir()) 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()) self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir())
for pcomar in self.metadata.package.providesComar: for pcomar in self.metadata.package.providesComar:
fpath = os.path.join(ctx.const.comar_dir, pcomar.script) fpath = os.path.join(ctx.const.comar_dir, pcomar.script)
# comar prefix is added to the pkg_dir while extracting comar # comar prefix is added to the pkg_dir while extracting comar
# script file. so we'll use pkg_dir as destination. # 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()) self.package.extract_file(fpath, self.package.pkg_dir())
def register_comar_scripts(self): def register_comar_scripts(self):
@@ -160,7 +160,7 @@ class Installer:
for pcomar in self.metadata.package.providesComar: for pcomar in self.metadata.package.providesComar:
scriptPath = os.path.join(self.package.comar_dir(),pcomar.script) 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 # FIXME: We must check the result of the command (possibly
# with id?) # with id?)
if comard: if comard:
+1 -1
View File
@@ -51,7 +51,7 @@ class LockedDBShelf(shelve.DBShelf):
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError: except IOError:
import sys 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) sys.exit(1)
return self.db.open(filename, dbname, filetype, flags, mode) return self.db.open(filename, dbname, filetype, flags, mode)
+4 -4
View File
@@ -7,8 +7,9 @@
# #
# Please read the COPYING file. # Please read the COPYING file.
# #
#
# Package Operations: install/remove/upgrade "Package Operations: install/remove/upgrade"
import os import os
@@ -46,7 +47,6 @@ def remove_single(package_name):
if comard: if comard:
# FIXME: (return value)... # FIXME: (return value)...
comard.remove(package_name) comard.remove(package_name)
ctx.ui.info('.\n')
def install_single(pkg, upgrade = False): def install_single(pkg, upgrade = False):
"""install a single package from URI or ID""" """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()), pkg_path = os.path.join(os.path.dirname(repo.indexuri.get_uri()),
str(pkg_uri.path())) 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! # Package will handle remote file for us!
install_single_file(pkg_path, upgrade) install_single_file(pkg_path, upgrade)
else: 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 # deneme, don't remove ulan
class AtomicOperation(object): class AtomicOperation(object):
+1 -1
View File
@@ -42,7 +42,7 @@ class PackageDB(object):
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, e: except IOError, e:
import sys import sys
ctx.ui.error("PackageDB: %s\n" % e) ctx.ui.error("PackageDB: %s" % e)
sys.exit(1) sys.exit(1)
self.d = shelve.open(self.fname) self.d = shelve.open(self.fname)
self.dr = shelve.open(self.fname2) self.dr = shelve.open(self.fname2)
+3 -4
View File
@@ -9,12 +9,11 @@
# #
# Please read the COPYING file. # Please read the COPYING file.
# #
# Authors: Baris Metin <baris@uludag.org.tr>
# Eray Ozkural <eray@uludag.org.tr>
# python standard library # python standard library
# Authors: Baris Metin <baris@uludag.org.tr
# Eray Ozkural <eray@uludag.org.tr>
from os.path import join from os.path import join
from os import access, R_OK from os import access, R_OK
@@ -55,7 +54,7 @@ class SourceArchive:
# check hash # check hash
if util.check_file_hash(self.archiveFile, self.archiveSHA1): if util.check_file_hash(self.archiveFile, self.archiveSHA1):
if interactive: if interactive:
ctx.ui.info('%s [cached]\n' % self.archiveName) ctx.ui.info('%s [cached]' % self.archiveName)
return True return True
return False return False
+1 -1
View File
@@ -74,7 +74,7 @@ class SourceFetcher(object):
def fetch(self, appendDest=""): def fetch(self, appendDest=""):
from fetcher import fetch_url 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) dest = join(self.dest, appendDest)
fetch_url(self.url, dest) fetch_url(self.url, dest)
+12 -6
View File
@@ -9,13 +9,19 @@
# #
# Please read the COPYING file. # 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> # 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 # standard python modules
from os.path import basename from os.path import basename
+50 -54
View File
@@ -7,63 +7,18 @@
# #
# Please read the COPYING file. # Please read the COPYING file.
# #
# generic user interface # generic user interface
#
# Authors: Eray Ozkural <eray@uludag.org.tr> # Authors: Eray Ozkural <eray@uludag.org.tr>
# Murat Eren <meren@uludag.org.tr> # Murat Eren <meren@uludag.org.tr>
import sys import sys
from pisi.colors import colorize import pisi
import pisi.context as ctx
class UI(object):
class CLI: "Abstract class for UI operations, derive from this."
"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 Progress: class Progress:
def __init__(self, totalsize): def __init__(self, totalsize):
@@ -81,8 +36,49 @@ class CLI:
else: else:
return 0 return 0
def display_progress(self, pd): def __init__(self, debuggy = False, verbose = False):
out = '\r%-30.30s %3d%% %12.2f %s' % \ self.show_debug = debuggy
(pd['filename'], pd['percent'], pd['rate'], pd['symbol']) self.show_verbose = verbose
self.info(out)
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
View File
@@ -363,7 +363,7 @@ def clean_locks(top = '.'):
for fn in files: for fn in files:
if fn.endswith('.lock'): if fn.endswith('.lock'):
path = os.path.join(root, fn) path = os.path.join(root, fn)
ctx.ui.info('Removing lock %s\n', path) ctx.ui.info('Removing lock %s', path)
os.unlink(path) os.unlink(path)
######################################## ########################################
+124 -53
View File
@@ -24,7 +24,8 @@
in xml.dom :( ) in xml.dom :( )
autoxml is a metaclass for automatic XML translation, using 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, Method names are mixedCase for compatibility with minidom,
an old library. an old library.
@@ -38,22 +39,23 @@ import codecs
# PiSi # PiSi
import pisi import pisi
from pisi.xmlext import * from pisi.xmlext import *
import pisi.context as ctx
class Error(pisi.Error): class Error(pisi.Error):
pass pass
import types import types
mandatory, optional = range(2) # poor man's enum mandatory, optional = range(2) # poor man's enum
# basic types # basic types
Text = types.StringType Text = types.StringType
Integer = types.IntType Integer = types.IntType
class LocalText: class LocalText(object):
"handle tags with localized text" """Handles XML tags/attributes with localized text"""
def __init__(): def __init__():
locs = {} locs = {}
@@ -80,17 +82,89 @@ class LocalText:
if not locs.has_key(L): if not locs.has_key(L):
L = 'en' L = 'en'
if not locs.has_key(L): 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 ""
return locs[L] return locs[L]
class autoxml(type): 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): 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) bases = list(bases)
if not XmlFile in bases: if not XmlFile in bases:
bases.append(XmlFile) bases.append(XmlFile)
@@ -98,7 +172,7 @@ class autoxml(type):
# standard initialization # standard initialization
super(autoxml, cls).__init__(name, bases, dict) super(autoxml, cls).__init__(name, bases, dict)
# initialize class attribute __xml_tags #TODO: initialize class attribute __xml_tags
#setattr(cls, 'xml_variables', []) #setattr(cls, 'xml_variables', [])
if not dict.has_key('tag'): if not dict.has_key('tag'):
@@ -119,12 +193,15 @@ class autoxml(type):
x = autoxml.gen_tag(cls, name) x = autoxml.gen_tag(cls, name)
elif var.startswith('a_'): elif var.startswith('a_'):
x = autoxml.gen_attr(cls, name) x = autoxml.gen_attr(cls, name)
ctx.ui.debug(x)
(init, decoder, encoder, formatter) = x (init, decoder, encoder, formatter) = x
inits.append(init) inits.append(init)
decoders.append(decoder) decoders.append(decoder)
encoders.append(encoder) encoders.append(encoder)
formatters.append(formatter) formatters.append(formatter)
varname = cls.mixed_case(name)
setattr(cls, 'initializers', inits) setattr(cls, 'initializers', inits)
def initialize(self): def initialize(self):
for init in self.__class__.initializers: for init in self.__class__.initializers:
@@ -134,13 +211,13 @@ class autoxml(type):
setattr(cls, 'decoders', decoders) setattr(cls, 'decoders', decoders)
def decode(self, node): def decode(self, node):
for decoder in self.__class__.decoders: for decoder in self.__class__.decoders:
decoder(self, node) setattr(self, varname, decoder(self, node))
setattr(cls, 'decode', decode) setattr(cls, 'decode', decode)
setattr(cls, 'encoders', encoders) setattr(cls, 'encoders', encoders)
def encode(self, xml): def encode(self, xml):
for encoder in self.__class__.encoders: for encoder in self.__class__.encoders:
encoder(self, xml) encoder(self, xml, getattr(self, varname))
setattr(cls, 'encode', encode) setattr(cls, 'encode', encode)
setattr(cls, 'formatters', formatters) setattr(cls, 'formatters', formatters)
@@ -183,7 +260,9 @@ class autoxml(type):
return identifier_p return identifier_p
def gen_basic(cls, token, val, readtext, writetext): 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) name = cls.mixed_case(token)
token_type = val[0] token_type = val[0]
req = val[1] req = val[1]
@@ -198,25 +277,23 @@ class autoxml(type):
try: try:
value = autoxml.basic_cons_map[token_type](text) value = autoxml.basic_cons_map[token_type](text)
except Error: except Error:
return ['Type mismatch'] raise Error('Type mismatch')
setattr(self, name, value) setattr(self, name, value)
else: else:
if req == mandatory: if req == mandatory:
return ['Mandatory argument not available'] raise Error('Mandatory argument not available')
else: else:
setattr(self, name, None)
return None return None
def encode(self, xml): def encode(self, xml, value):
node = xml.newNode(cls.tag) node = xml.newNode(cls.tag)
if hasattr(self, name): if hasattr(self, name):
writetext(xml, node, token, str(getattr(self, name))) writetext(xml, node, token, str(value))
else: else:
if req == mandatory: if req == mandatory:
return ['Mandatory argument not available'] raise Error('Mandatory argument not available')
def format(self): def format(self):
#print 'format:', name
if hasattr(self, name): if hasattr(self, name):
return '%s: %s\n' % (token, str(getattr(self, name))) return '%s: %s\n' % (token, str(getattr(self, name)))
else: else:
@@ -226,69 +303,63 @@ class autoxml(type):
return initialize, decode, encode, format 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""" """generate an attribute with a basic datatype"""
def readtext(node, attr): def readtext(node, attr):
return getNodeAttribute(node, attr) return getNodeAttribute(node, attr)
def writetext(xml, node, attr, value): def writetext(xml, node, attr, value):
node.setAttribute(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""" """generate a tag with a basic datatype"""
def readtext(node, tag): def readtext(node, tag):
return getNodeText(getNode(node, tag)) return getNodeText(getNode(node, tag))
def writetext(xml, node, tag, value): def writetext(xml, node, tag, value):
xml.addTextNodeUnder(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): def gen_class_tag(cls, tag, val):
pass pass
def gen_list_tag(cls, tag, val): def gen_list_tag(cls, tag, val):
name = cls.mixed_case(token) name = cls.mixed_case(tag)
token_type = val[0] tag_type = val[0]
req = val[1] req = val[1]
if len(val)>=3: if len(val)>=3:
path = val[2] path = val[2] # an alternative path specified
else: else:
path = tag path = tag # otherwise it's the same name as
if len(token_type!=1): # the tag
raise Error('List type must contain one element') if len(tag_type) != 1:
raise Error('List type must contain only one element')
x = autoxml.gen_tag_aux(cls, tag, tag_type, val) x = autoxml.gen_tag_aux(cls, tag, tag_type[0], val)
(x_init, x_decoder, x_encoder, x_formatter) = x (init_item, decode_item, encode_item, format_item) = x
def init(self): def init(self):
varname = cls.mixed_case(name) setattr(self, name, [])
setattr(self, varname, [])
def decode(self, node): def decode(self, node):
nodes = getAllNodes(node) l = []
if len(nodes) is 0 and req is mandatory: self.nodes = getAllNodes(node, path)
pass print 'F U', self.nodes
#FIXME: add error here if len(self.nodes) is 0 and req is mandatory:
for node in nodes: raise Error('Mandatory list empty')
x_decoder(self, node) for node in self.nodes:
pass l.append(decode_item(self, node))
return l
def encode(self, xml): def encode(self, xml, value):
node = xml.newNode(cls.tag) pass
if hasattr(self, name):
writetext(xml, node, token, str(getattr(self, name)))
else:
if req == mandatory:
return ['Mandatory argument not available']
def format(self): def format(self):
#print 'format:', name #print 'format:', name
if hasattr(self, name): s = ''
return '%s: %s\n' % (token, str(getattr(self, name))) l = getattr(self, name)
else: for ix in range(len(l)):
if req == mandatory: s += str(ix) + format_item(l[ix])
raise Error('Mandatory variable %s not available' % name) return s
return ""
return (init, decode, encode, format) return (init, decode, encode, format)