* 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 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)
+ 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
+1 -1
View File
@@ -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"))
+1 -1
View File
@@ -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):
+3 -3
View File
@@ -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):
+2 -2
View File
@@ -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'''
+19 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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')
+2
View File
@@ -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.
+64
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+3 -4
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)