1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-03-03 14:42:11 +01:00

Merged revisions 3774-3775,3779,3781-3782,3787,3789-3793,3798,3802,3805,3817-3822,3827,3829-3830,3837-3839,3841-3842,3848-3850 via svnmerge from

https://forgesvn1.novell.com/svn/opensuse/branches/buildservice/osc-exception-handling

........
  r3774 | poeml | 2008-04-22 17:39:45 +0200 (Tue, 22 Apr 2008) | 3 lines
  
  A first draft on implement systematic exception handling: 
  Add errors.py with some exceptions defined, and babysitter.py to handle them
........
  r3775 | poeml | 2008-04-22 22:34:12 +0200 (Tue, 22 Apr 2008) | 9 lines
  
  - new global options:
       --debugger         jump into the debugger before executing anything
       --post-mortem      jump into the debugger in case of errors
       -t, --traceback    print call trace in case of errors
  - traceback and post_mortem can also be set in .oscrc.
  - catch more errors (HTTPError).
  - make config accessible from outside of the Osc instance, by making it a class
    attribute
........
  r3779 | poeml | 2008-04-23 00:55:49 +0200 (Wed, 23 Apr 2008) | 5 lines
  
  - new global option:
       -d, --debug        print info useful for debugging
  - catch some more errors (HTTPError), with OscHTTPError which isn't very
    advanced yet.
........
  r3781 | Marcus_H | 2008-04-23 01:02:00 +0200 (Wed, 23 Apr 2008) | 2 lines
  
  - added OscConfigError class (just for testing).
  - small change in the signature of the OscBaseError constructor (actually we should get rid of the 'args' tuple because it will be deprecated sooner or later
........
  r3782 | Marcus_H | 2008-04-23 02:02:13 +0200 (Wed, 23 Apr 2008) | 2 lines
  
  - access the prg.conf.config dict in a save way - this way we avoid AttributeErrors if the prg.conf.config dict doesn't exist
  - in case of an configparser error we have to use the prg.options object directly (this was removed in r3781 by accident)
........
  r3787 | poeml | 2008-04-23 09:23:56 +0200 (Wed, 23 Apr 2008) | 4 lines
  
  - remove local exception handling from do_req
  - for HTTPError, print details (headers and response) when in debug mode
  - catch AttributeError
........
  r3789 | poeml | 2008-04-23 16:23:14 +0200 (Wed, 23 Apr 2008) | 4 lines
  
  - errors: add two new classes named OscWrongOptionsError and OscWrongArgsError
  - commandline: raise instances of the new errors in a number of places
  - commandline: add get_cmd_help() to Osc instance which returns the formatted help of a subcommand
........
  r3790 | Marcus_H | 2008-04-23 16:48:28 +0200 (Wed, 23 Apr 2008) | 4 lines
  
  - added 2 new exception classes: OscNoConfigfileError and OscIOError
  - added new method write_config() to the conf.py module: This method writes osc's configuration file
  - minor cleanups in the conf module
........
  r3791 | poeml | 2008-04-23 17:11:07 +0200 (Wed, 23 Apr 2008) | 3 lines
  
  small compatibility fix for r3790: try-except-finally isn't supported in
  python-2.4.2, thus do the same as try-except inside a try-finally.
........
  r3792 | poeml | 2008-04-23 17:37:53 +0200 (Wed, 23 Apr 2008) | 2 lines
  
  fix up the remaining places regarding handling of errors related to commandline parsing
........
  r3793 | poeml | 2008-04-23 17:40:34 +0200 (Wed, 23 Apr 2008) | 3 lines
  
  raise a NoWorkingCopyError in osc.core.store_read_project() in case of an
  IOError
........
  r3798 | Marcus_H | 2008-04-23 23:55:24 +0200 (Wed, 23 Apr 2008) | 1 line
  
  ported -r3797 from trunk
........
  r3802 | Marcus_H | 2008-04-24 11:00:55 +0200 (Thu, 24 Apr 2008) | 1 line
  
  ported -r3801 from trunk
........
  r3805 | poeml | 2008-04-24 12:52:30 +0200 (Thu, 24 Apr 2008) | 2 lines
  
  raise OscHTTPError in show_pattern_meta(), replacing local error handling
........
  r3817 | poeml | 2008-04-24 20:21:32 +0200 (Thu, 24 Apr 2008) | 9 lines
  
  - remove errors.OscHTTPError again.
    it seems simpler to use urllib2.HTTPError instead (and just add a specific
    error text message where appropriate, and re-raise)
  - for 404s, check out _which_ part was not found
    it is very ugly, but may be considered Good for pragmatic reasons
  - removed local exception handling and workaround for returned 500's from
    delete_package() and delete_project(), thereby getting rid of 4 possible exit
    points.
........
  r3818 | Marcus_H | 2008-04-24 22:36:17 +0200 (Thu, 24 Apr 2008) | 1 line
  
  - this check is superfluous because every HTTPError instance has a code attribute
........
  r3819 | poeml | 2008-04-25 00:39:39 +0200 (Fri, 25 Apr 2008) | 1 line
  
  remove a forgotten debug line from core.delete_project()
........
  r3820 | poeml | 2008-04-25 10:07:58 +0200 (Fri, 25 Apr 2008) | 2 lines
  
  - ditch local error handling from wipebinaries(), rebuild(), and abortbuild()
........
  r3821 | poeml | 2008-04-25 10:56:38 +0200 (Fri, 25 Apr 2008) | 2 lines
  
  It is never needed to import the exception module.
........
  r3822 | poeml | 2008-04-25 11:13:39 +0200 (Fri, 25 Apr 2008) | 4 lines
  
  - when going into the debugger with --post-mortem, always print a traceback before
    (thus implying -t)
  - do not jump into the debugger if not on a TTY, or working in interactive mode
........
  r3827 | poeml | 2008-04-25 13:07:46 +0200 (Fri, 25 Apr 2008) | 9 lines
  
  - add errors.OscWorkingCopyOutdated, which takes a tuple with three args:
    path to working copy, current rev, expected rev
  - add handler for urllib2.URLError errors to the babysitter
  - simplify the OscWrongArgsError and OscWrongOptionsError handlers, by removing
    the extra line "Sorry, wrong ..." that was printed before the messages given
    when the error was raised.
  - remove one more errors.OscHTTPError which was still there, and raise
    urllib2.HTTPError instead (show_package_meta())
........
  r3829 | poeml | 2008-04-25 14:19:10 +0200 (Fri, 25 Apr 2008) | 11 lines
  
  - comment some methods in osc.core which are used by nearly all do_* methods in
    osc.commandline
  - improve "is not a package/project dir" error messages, by printing the
    absolute path tried, instead of '.' for the cwd
  - make core.store_read_package() raise a proper NoWorkingCopyError instead of
    terminating
  - give attribution to things in babysitter.py copied from mercurial
  - prefix HTTPError exceptions with 'Server returned an error:'
  - remove obsolete local error handling from do_prjresults(), do_importsrcpkg(),
    do_repos()
........
  r3830 | poeml | 2008-04-25 14:29:35 +0200 (Fri, 25 Apr 2008) | 1 line
  
  catch IOError exceptions in the babysitter
........
  r3837 | poeml | 2008-04-25 17:27:36 +0200 (Fri, 25 Apr 2008) | 5 lines
  
  - do_remotebuildlog: raise errors for wrong arguments, remove exits
  - raise AttributeError in make_meta_url() instead of exiting
  - delete unused method core.delete_server_files()
  - replace exit call inside make_meta_url() with an AttributeError 
........
  r3838 | poeml | 2008-04-25 17:49:18 +0200 (Fri, 25 Apr 2008) | 1 line
  
  simplify the check in do_checkout if a project exists, by using show_project_meta() instead of meta_exists
........
  r3839 | poeml | 2008-04-25 18:31:26 +0200 (Fri, 25 Apr 2008) | 6 lines
  
  - commandline do_checkout(): change the order of the two checks, first do the
    (cheaper) check for existing directory
  - core.core checkout_package(): simplify the check in if the package
    exists, by using show_package_meta() instead of meta_exists
    Let it throw an exception, instead of using sys.exit().
........
  r3841 | Marcus_H | 2008-04-27 15:48:06 +0200 (Sun, 27 Apr 2008) | 5 lines
  
  - added 2 new exception classes: PackageError() and PackageExistsError. The PackageError() class is meant to be the base class for all subsequent package exceptions.
  - get rid of 2 sys.exit(1) calls
  - make the update() method of the Project() class safer: in any case we have to write the _packages file otherwise the package tracking will be screwed up.
  - minor fix in delPackage(): use getTransActPath() when printing out the filename
........
  r3842 | Marcus_H | 2008-04-27 16:52:55 +0200 (Sun, 27 Apr 2008) | 3 lines
  
  - make the commit() method safe: in any case we have to write the _packages file otherwise the package tracking will be screwed up.
  - removed another sys.exit(1): raise an exception if a package is missing when doing a commit. For now we use the PackageExistsError() exception but this might change in the future (updated description of PackageExistsError() according to this change)
........
  r3848 | poeml | 2008-04-28 12:46:45 +0200 (Mon, 28 Apr 2008) | 3 lines
  
  rename several error classes, dropping the "Osc" prefix, and "Error" suffix in
  cases where they don't really make sense.
........
  r3849 | poeml | 2008-04-28 12:57:32 +0200 (Mon, 28 Apr 2008) | 3 lines
  
  - rename osc.errors module to osc.oscerr, to make it easier to import it from
    other programs and have a crystal clear namespace
........
  r3850 | poeml | 2008-04-28 13:26:12 +0200 (Mon, 28 Apr 2008) | 2 lines
  
  split PackageExists exception class into PackageExists and PackageMissing
........
This commit is contained in:
Dr. Peter Poeml 2008-04-28 16:37:44 +00:00
parent f8b35ba80a
commit 3018460596
7 changed files with 584 additions and 357 deletions

View File

@ -5,8 +5,12 @@
import sys import sys
from osc import commandline from osc import commandline
from osc import babysitter
osccli = commandline.Osc()
r = babysitter.run(osccli)
sys.exit(r)
osc = commandline.Osc()
sys.exit( osc.main() )

View File

@ -1 +1 @@
__all__ = ['core', 'commandline', 'othermethods', 'build', 'fetch', 'meter'] __all__ = ['babysitter', 'core', 'commandline', 'oscerr', 'othermethods', 'build', 'fetch', 'meter']

145
osc/babysitter.py Normal file
View File

@ -0,0 +1,145 @@
#!/usr/bin/python
# Copyright (C) 2008 Peter Poeml / Novell Inc. All rights reserved.
# This program is free software; it may be used, copied, modified
# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
import sys
import signal
from osc import oscerr
from urllib2 import URLError, HTTPError
# the good things are stolen from Matt Mackall's mercurial
def catchterm(*args):
raise oscerr.SignalInterrupt
for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
num = getattr(signal, name, None)
if num: signal.signal(num, catchterm)
def run(prg):
try:
try:
if '--debugger' in sys.argv:
import pdb
pdb.set_trace()
# here we actually run the program:
return prg.main()
except:
# look for an option in the prg.options object and in the config dict
# print stack trace, if desired
if getattr(prg.options, 'traceback', None) or getattr(prg.conf, 'config', {}).get('traceback', None) or \
getattr(prg.options, 'post_mortem', None) or getattr(prg.conf, 'config', {}).get('post_mortem', None):
import traceback
traceback.print_exc(file=sys.stderr)
# we could use http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52215
# enter the debugger, if desired
if getattr(prg.options, 'post_mortem', None) or getattr(prg.conf, 'config', {}).get('post_mortem', None):
if sys.stdout.isatty() and not hasattr(sys, 'ps1'):
import pdb
pdb.post_mortem(sys.exc_info()[2])
else:
print >>sys.stderr, 'sys.stdout is not a tty. Not jumping into pdb.'
raise
except oscerr.SignalInterrupt:
print >>sys.stderr, 'killed!'
return 1
except KeyboardInterrupt:
print >>sys.stderr, 'interrupted!'
return 1
except oscerr.UnreadableFile, e:
print >>sys.stderr, e.msg
return 1
except (oscerr.NoWorkingCopy, oscerr.WorkingCopyWrongVersion), e:
print >>sys.stderr, e
return 1
except HTTPError, e:
print >>sys.stderr, 'Server returned an error:', e
if hasattr(e, 'osc_msg'):
print >>sys.stderr, e.osc_msg
if getattr(prg.options, 'debug', None) or \
getattr(prg.conf, 'config', {}).get('debug', None):
print >>sys.stderr, e.hdrs
print >>sys.stderr, e.read()
# For 404s, check out _which_ part was not found
#
# It is very ugly, but may be Good for pragmatic reasons
#
# FIXME this can be obsoleted when the api returns a more specific error code
# like "project not found" right away
if e.code == 404:
import urlparse
scheme, netloc, path = urlparse.urlsplit(e.url)[0:3]
parts = path.split('/')
# we know how to do this for /source URLs. I don't know for others right now...
if len(parts) > 1 and parts[1] == 'source':
if len(parts) == 3:
# it was a request on /source/project
print >>sys.stderr, 'A project named \'%s\' does not exist.' % parts[2]
if len(parts) == 4:
# it was a request on /source/project/package
prjurl = urlparse.urlunsplit((scheme, netloc, '/'.join(parts[:3]), None, None))
import osc.core
try:
osc.core.http_GET(prjurl)
except:
print >>sys.stderr, 'A project named \'%s\' does not exist.' % parts[2]
else:
print >>sys.stderr, \
'A package named \'%s\' does not exist in project \'%s\'.' \
% (parts[3], parts[2])
else:
# not handled...
pass
return 1
except URLError, e:
print >>sys.stderr, 'Failed to reach a server:', e.reason
return 1
except (oscerr.ConfigError, oscerr.NoConfigfile), e:
print >>sys.stderr, e.msg
return 1
except oscerr.OscIOError, e:
print >>sys.stderr, e.msg
if getattr(prg.options, 'debug', None) or \
getattr(prg.conf, 'config', {}).get('debug', None):
print >>sys.stderr, e.e
return 1
except (oscerr.WrongOptions, oscerr.WrongArgs), e:
print >>sys.stderr, e
return 2
except oscerr.WorkingCopyOutdated, e:
print >>sys.stderr, e
return 1
except (oscerr.PackageExists, oscerr.PackageMissing), e:
print >>sys.stderr, e.msg
return 1
except IOError, e:
print >>sys.stderr, e
return 1
except AttributeError, e:
print >>sys.stderr, e
return 1

View File

@ -9,6 +9,7 @@
from core import * from core import *
import cmdln import cmdln
import conf import conf
import oscerr
class Osc(cmdln.Cmdln): class Osc(cmdln.Cmdln):
@ -26,6 +27,7 @@ class Osc(cmdln.Cmdln):
* http://www.opensuse.org/Build_Service/CLI * http://www.opensuse.org/Build_Service/CLI
""" """
name = 'osc' name = 'osc'
conf = None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -37,8 +39,16 @@ class Osc(cmdln.Cmdln):
"""this is the parser for "global" options (not specific to subcommand)""" """this is the parser for "global" options (not specific to subcommand)"""
optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version()) optparser = cmdln.CmdlnOptionParser(self, version=get_osc_version())
optparser.add_option('--debugger', action='store_true',
help='jump into the debugger before executing anything')
optparser.add_option('--post-mortem', action='store_true',
help='jump into the debugger in case of errors')
optparser.add_option('-t', '--traceback', action='store_true',
help='print call trace in case of errors')
optparser.add_option('-H', '--http-debug', action='store_true', optparser.add_option('-H', '--http-debug', action='store_true',
help='debug HTTP traffic') help='debug HTTP traffic')
optparser.add_option('-d', '--debug', action='store_true',
help='print info useful for debugging')
optparser.add_option('-A', '--apisrv', dest='apisrv', optparser.add_option('-A', '--apisrv', dest='apisrv',
metavar='URL', metavar='URL',
help='specify URL to access API server at') help='specify URL to access API server at')
@ -50,10 +60,41 @@ class Osc(cmdln.Cmdln):
def postoptparse(self): def postoptparse(self):
"""merge commandline options into the config""" """merge commandline options into the config"""
try:
conf.get_config(override_conffile = self.options.conffile, conf.get_config(override_conffile = self.options.conffile,
override_apisrv = self.options.apisrv,
override_debug = self.options.debug,
override_http_debug = self.options.http_debug, override_http_debug = self.options.http_debug,
override_apisrv = self.options.apisrv) override_traceback = self.options.traceback,
override_post_mortem = self.options.post_mortem)
except oscerr.NoConfigfile, e:
print >>sys.stderr, e.msg
print >>sys.stderr, 'Creating osc configuration file %s ...' % e.file
import getpass
config = {}
config['user'] = raw_input('Username: ')
config['pass'] = getpass.getpass()
if conf.write_config(e.file, config):
print >>sys.stderr, 'done'
conf.get_config(override_conffile = self.options.conffile,
override_apisrv = self.options.apisrv,
override_debug = self.options.debug,
override_http_debug = self.options.http_debug,
override_traceback = self.options.traceback,
override_post_mortem = self.options.post_mortem)
else:
raise NoConfigfile(e.file, 'Unable to create osc\'s configuration file \
\'%s\'' % e.file)
self.conf = conf
def get_cmd_help(self, cmdname):
doc = self._get_cmd_handler(cmdname).__doc__
doc = self._help_reindent(doc)
doc = self._help_preprocess(doc, cmdname)
doc = doc.rstrip() + '\n' # trim down trailing space
return self._str(doc)
def do_init(self, subcmd, opts, project, package): def do_init(self, subcmd, opts, project, package):
@ -117,15 +158,13 @@ class Osc(cmdln.Cmdln):
package = args[1] package = args[1]
if opts.binaries and (not opts.repo or not opts.arch): if opts.binaries and (not opts.repo or not opts.arch):
sys.exit('missing options:\n' raise oscerr.WrongOptions('Sorry, -r <repo> -a <arch> missing\n'
'-r <repo> -a <arch>\n' 'You can list repositories with: \'osc platforms <project>\'')
'list repositories with:\n'
'\'osc platforms %s\'' %project)
# list binaries # list binaries
if opts.binaries: if opts.binaries:
if not args: if not args:
sys.exit('there are no binaries to list above project level.') raise oscerr.WrongArgs('There are no binaries to list above project level.')
elif len(args) == 1: elif len(args) == 1:
#if opts.verbose: #if opts.verbose:
@ -144,7 +183,7 @@ class Osc(cmdln.Cmdln):
elif len(args) == 1: elif len(args) == 1:
if opts.verbose: if opts.verbose:
sys.exit('The verbose option is not implemented for projects.') raise oscerr.WrongOptions('Sorry, the --verbose option is not implemented for projects.')
print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project)) print '\n'.join(meta_get_packagelist(conf.config['apiurl'], project))
@ -211,8 +250,8 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if not args or args[0] not in metatypes.keys(): if not args or args[0] not in metatypes.keys():
print >>sys.stderr, 'Unknown meta type. Choose one of %s.' % ', '.join(metatypes) raise oscerr.WrongArgs('Unknown meta type. Choose one of %s.' \
return 2 % ', '.join(metatypes))
cmd = args[0] cmd = args[0]
del args[0] del args[0]
@ -224,11 +263,9 @@ class Osc(cmdln.Cmdln):
else: else:
min_args, max_args = 1, 1 min_args, max_args = 1, 1
if len(args) < min_args: if len(args) < min_args:
print >>sys.stderr, 'Too few arguments.' raise oscerr.WrongArgs('Too few arguments.')
return 2
if len(args) > max_args: if len(args) > max_args:
print >>sys.stderr, 'Too many arguments.' raise oscerr.WrongArgs('Too many arguments.')
return 2
# specific arguments # specific arguments
if cmd == 'prj': if cmd == 'prj':
@ -247,7 +284,7 @@ class Osc(cmdln.Cmdln):
pattern = None pattern = None
# enforce pattern argument if needed # enforce pattern argument if needed
if opts.edit or opts.file: if opts.edit or opts.file:
sys.exit('a pattern file argument is required.') raise oscerr.WrongArgs('A pattern file argument is required.')
# show # show
if not opts.edit and not opts.file and not opts.delete: if not opts.edit and not opts.file and not opts.delete:
@ -395,8 +432,8 @@ class Osc(cmdln.Cmdln):
cmds = ['create', 'list', 'show', 'decline', 'accept'] cmds = ['create', 'list', 'show', 'decline', 'accept']
if not args or args[0] not in cmds: if not args or args[0] not in cmds:
print >>sys.stderr, 'Unknown submitreq action. Choose one of %s.' % ', '.join(cmds) raise oscerr.WrongArgs('Unknown submitreq action. Choose one of %s.' \
return 2 % ', '.join(cmds))
cmd = args[0] cmd = args[0]
del args[0] del args[0]
@ -408,11 +445,9 @@ class Osc(cmdln.Cmdln):
else: else:
min_args, max_args = 1, 1 min_args, max_args = 1, 1
if len(args) < min_args: if len(args) < min_args:
print >>sys.stderr, 'Too few arguments.' raise oscerr.WrongArgs('Too few arguments.')
return 2
if len(args) > max_args: if len(args) > max_args:
print >>sys.stderr, 'Too many arguments.' raise oscerr.WrongArgs('Too many arguments.')
return 2
# collect specific arguments # collect specific arguments
if cmd == 'create': if cmd == 'create':
@ -459,7 +494,8 @@ class Osc(cmdln.Cmdln):
r.dst_project, r.dst_package, None, r.dst_project, r.dst_package, None,
r.src_project, r.src_package, r.src_md5) r.src_project, r.src_package, r.src_md5)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
print >>sys.stderr, 'Diff not possible:', e e.osc_msg = 'Diff not possible'
raise
# decline # decline
@ -521,9 +557,8 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if not args or len(args) < 3: if not args or len(args) < 3:
print >>sys.stderr, 'Incorrect number of argument.' raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
self.do_help([None, 'linkpac']) + self.get_cmd_help('linkpac'))
return 2
src_project = args[0] src_project = args[0]
src_package = args[1] src_package = args[1]
@ -552,9 +587,8 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if not args or len(args) < 3: if not args or len(args) < 3:
print >>sys.stderr, 'Incorrect number of argument.' raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
self.do_help([None, 'aggregatepac']) + self.get_cmd_help('aggregatepac'))
return 2
src_project = args[0] src_project = args[0]
src_package = args[1] src_package = args[1]
@ -595,9 +629,8 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if not args or len(args) < 3: if not args or len(args) < 3:
print >>sys.stderr, 'Incorrect number of argument.' raise oscerr.WrongArgs('Incorrect number of arguments.\n\n' \
self.do_help([None, 'copypac']) + self.get_cmd_help('copypac'))
return 2
src_project = args[0] src_project = args[0]
src_package = args[1] src_package = args[1]
@ -616,8 +649,7 @@ class Osc(cmdln.Cmdln):
if src_project == dst_project and \ if src_project == dst_project and \
src_package == dst_package and \ src_package == dst_package and \
src_apiurl == dst_apiurl: src_apiurl == dst_apiurl:
print >>sys.stderr, 'Error: source and destination are the same.' raise oscerr.WrongArgs('Source and destination are the same.')
return 1
if src_apiurl != dst_apiurl: if src_apiurl != dst_apiurl:
opts.client_side_copy = True opts.client_side_copy = True
@ -679,7 +711,6 @@ class Osc(cmdln.Cmdln):
specfile = None specfile = None
pacs = findpacs(args) pacs = findpacs(args)
for p in pacs: for p in pacs:
p.read_meta_from_spec(specfile) p.read_meta_from_spec(specfile)
p.update_package_meta() p.update_package_meta()
@ -823,24 +854,22 @@ class Osc(cmdln.Cmdln):
rev, expand_link=opts.expand_link, prj_dir=project) rev, expand_link=opts.expand_link, prj_dir=project)
elif project: elif project:
if not os.path.exists(project): if os.path.exists(project):
if meta_exists(metatype='prj', path_args=quote_plus(project), create_new=False): sys.exit('osc: project \'%s\' already exists' % project)
# check if the project does exist (show_project_meta will throw an exception)
show_project_meta(conf.config['apiurl'], project)
init_project_dir(conf.config['apiurl'], project, project) init_project_dir(conf.config['apiurl'], project, project)
print statfrmt('A', project) print statfrmt('A', project)
else:
print >>sys.stderr, 'osc: project \'%s\' does not exist on the server' % project
sys.exit(1)
else:
print >>sys.stderr, 'osc: project \'%s\' already exists' % project
sys.exit(1)
# all packages # all packages
for package in meta_get_packagelist(conf.config['apiurl'], project): for package in meta_get_packagelist(conf.config['apiurl'], project):
checkout_package(conf.config['apiurl'], project, package, checkout_package(conf.config['apiurl'], project, package,
expand_link=opts.expand_link, prj_dir=project) expand_link=opts.expand_link, prj_dir=project)
else: else:
print >>sys.stderr, 'Missing argument.' raise oscerr.WrongArgs('Missing argument.\n\n' \
self.do_help([None, 'checkout']) + self.get_cmd_help('checkout'))
return 2
@cmdln.option('-v', '--verbose', action='store_true', @cmdln.option('-v', '--verbose', action='store_true',
@ -897,8 +926,8 @@ class Osc(cmdln.Cmdln):
elif os.path.isfile(arg): elif os.path.isfile(arg):
pacpaths.append(arg) pacpaths.append(arg)
else: else:
print >>sys.stderr, 'osc: error: \'%s\' is neither a project or a package directory' % arg msg = '\'%s\' is neither a project or a package directory' % arg
return 1 raise oscerr.NoWorkingCopy, msg
lines = [] lines = []
# process single packages # process single packages
lines = getStatus(findpacs(pacpaths), None, opts.verbose) lines = getStatus(findpacs(pacpaths), None, opts.verbose)
@ -917,9 +946,8 @@ class Osc(cmdln.Cmdln):
${cmd_option_list} ${cmd_option_list}
""" """
if not args: if not args:
print >>sys.stderr, 'Missing argument.' raise oscerr.WrongArgs('Missing argument.\n\n' \
self.do_help([None, 'add']) + self.get_cmd_help('add'))
return 2
filenames = parseargs(args) filenames = parseargs(args)
#print filenames #print filenames
@ -938,8 +966,8 @@ class Osc(cmdln.Cmdln):
sys.exit(1) sys.exit(1)
if len(args) != 1: if len(args) != 1:
print >>sys.stderr, 'wrong number of arguments!' raise oscerr.WrongArgs('Wrong number of arguments.')
sys.exit(1)
createPackageDir(args[0]) createPackageDir(args[0])
@ -1098,8 +1126,8 @@ class Osc(cmdln.Cmdln):
if (opts.expand_link and opts.unexpand_link) \ if (opts.expand_link and opts.unexpand_link) \
or (opts.expand_link and opts.revision) \ or (opts.expand_link and opts.revision) \
or (opts.unexpand_link and opts.revision): or (opts.unexpand_link and opts.revision):
sys.exit('The options --expand-link, --unexpand-link and ' raise oscerr.WrongOptions('Sorry, the options --expand-link, --unexpand-link and '
'--revision are mutually exclusive') '--revision are mutually exclusive.')
if opts.revision and ( len(args) == 1): if opts.revision and ( len(args) == 1):
rev, dummy = parseRevisionOption(opts.revision) rev, dummy = parseRevisionOption(opts.revision)
@ -1140,9 +1168,8 @@ class Osc(cmdln.Cmdln):
""" """
if not args: if not args:
print >>sys.stderr, 'Missing argument.' raise oscerr.WrongArgs('Missing argument.\n\n' \
self.do_help([None, 'delete']) + self.get_cmd_help('delete'))
return 2
args = parseargs(args) args = parseargs(args)
# check if args contains a package which was removed by # check if args contains a package which was removed by
@ -1199,9 +1226,8 @@ class Osc(cmdln.Cmdln):
""" """
if not args: if not args:
print >>sys.stderr, 'Missing argument.' raise oscerr.WrongArgs('Missing argument.\n\n' \
self.do_help([None, 'resolved']) + self.get_cmd_help('resolved'))
return 2
args = parseargs(args) args = parseargs(args)
pacs = findpacs(args) pacs = findpacs(args)
@ -1296,12 +1322,8 @@ class Osc(cmdln.Cmdln):
else: else:
wd = os.curdir wd = os.curdir
try:
project = store_read_project(wd) project = store_read_project(wd)
apiurl = store_read_apiurl(wd) apiurl = store_read_apiurl(wd)
except:
print >>sys.stderr, '\'%s\' is neither an osc project or package directory' % wd
return 1
print '\n'.join(get_prj_results(apiurl, project, show_legend=opts.legend, csv=opts.csv)) print '\n'.join(get_prj_results(apiurl, project, show_legend=opts.legend, csv=opts.csv))
@ -1344,11 +1366,9 @@ class Osc(cmdln.Cmdln):
""" """
args = slash_split(args) args = slash_split(args)
if len(args) < 4: if len(args) < 4:
print >>sys.stderr, "too few arguments" raise oscerr.WrongArgs('Too few arguments.')
sys.exit(1)
elif len(args) > 4: elif len(args) > 4:
print >>sys.stderr, "too many arguments" raise oscerr.WrongArgs('Too many arguments.')
sys.exit(1)
print_buildlog(conf.config['apiurl'], *args) print_buildlog(conf.config['apiurl'], *args)
@ -1385,12 +1405,11 @@ class Osc(cmdln.Cmdln):
apiurl = store_read_apiurl(wd) apiurl = store_read_apiurl(wd)
if args is None or len(args) < 2: if args is None or len(args) < 2:
print >>sys.stderr, 'Missing argument.'
print 'Valid arguments for this package are:' print 'Valid arguments for this package are:'
print print
self.do_repos(None, None) self.do_repos(None, None)
print print
return 2 raise oscerr.WrongArgs('Missing argument')
platform = args[0] platform = args[0]
arch = args[1] arch = args[1]
@ -1450,10 +1469,6 @@ class Osc(cmdln.Cmdln):
""" """
args = parseargs(args) args = parseargs(args)
for arg in args:
if not is_project_dir(arg) and not is_package_dir(arg):
print >>sys.stderr, '\'%s\' is neither a package dir nor a project dir' % arg
args.remove(arg)
for arg in args: for arg in args:
for platform in get_repos_of_project(store_read_apiurl(arg), store_read_project(arg)): for platform in get_repos_of_project(store_read_apiurl(arg), store_read_project(arg)):
@ -1532,25 +1547,22 @@ class Osc(cmdln.Cmdln):
return 1 return 1
if len(args) == 2: if len(args) == 2:
print >>sys.stderr, 'Missing argument: build description (spec of dsc file)' raise oscerr.WrongArgs('Missing argument: build description (spec of dsc file)')
return 2
elif len(args) < 2: elif len(args) < 2:
print # we are going to raise an error for this, but first look up some helpful details:
print >>sys.stderr, 'Missing argument.' msg= ['You have to choose a repo to build on.']
print 'Valid arguments are:' msg.append('Possible repositories on this machine are:\n')
print 'you have to choose a repo to build on'
print 'possible repositories on this machine are:'
print
for platform in get_repos_of_project(store_read_apiurl(os.curdir), for platform in get_repos_of_project(store_read_apiurl(os.curdir),
store_read_project(os.curdir)): store_read_project(os.curdir)):
arch = platform.split()[1] # arch arch = platform.split()[1] # arch
if arch == osc.build.hostarch or \ if arch == osc.build.hostarch or \
arch in osc.build.can_also_build.get(osc.build.hostarch, []): arch in osc.build.can_also_build.get(osc.build.hostarch, []):
print platform.strip() msg.append(platform.strip())
return 2 raise oscerr.WrongArgs('Missing argument.\n\n' + '\n'.join(msg))
elif len(args) > 3: elif len(args) > 3:
print >>sys.stderr, 'too many arguments' raise oscerr.WrongArgs('Too many arguments')
return 2
if opts.prefer_pkgs: if opts.prefer_pkgs:
for d in opts.prefer_pkgs: for d in opts.prefer_pkgs:
@ -1634,9 +1646,7 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if len(args) < 1: if len(args) < 1:
print >>sys.stderr, 'Missing argument.' raise oscerr.WrongArgs('Missing argument.')
#self.do_help([None, 'rebuildpac'])
return 2
package = repo = arch = code = None package = repo = arch = code = None
project = args[0] project = args[0]
@ -1687,8 +1697,7 @@ class Osc(cmdln.Cmdln):
""" """
if len(args) < 1: if len(args) < 1:
print >>sys.stderr, 'Missing <project> argument' raise oscerr.WrongArgs('Missing <project> argument.')
return 2
if len(args) == 2: if len(args) == 2:
package = args[1] package = args[1]
@ -1722,8 +1731,7 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if len(args) < 1: if len(args) < 1:
print >>sys.stderr, 'Missing <project> argument' raise oscerr.WrongArgs('Missing <project> argument.')
return 2
if len(args) == 2: if len(args) == 2:
package = args[1] package = args[1]
@ -1773,11 +1781,9 @@ class Osc(cmdln.Cmdln):
""" """
if len(args) > 1: if len(args) > 1:
print >>sys.stderr, 'too many arguments' raise oscerr.WrongArgs('Too many arguments.')
sys.exit(1)
elif len(args) < 1: elif len(args) < 1:
print >>sys.stderr, 'too few arguments' raise oscerr.WrongArgs('Too few arguments.')
sys.exit(1)
search_list = [] search_list = []
search_for = [] search_for = []
@ -1872,10 +1878,6 @@ class Osc(cmdln.Cmdln):
else: else:
project_dir = os.curdir project_dir = os.curdir
if not is_project_dir(project_dir):
print >>sys.stderr, 'project dir \'%s\' does not exist' % project_dir
sys.exit(1)
else:
if conf.config['do_package_tracking']: if conf.config['do_package_tracking']:
project = Project(project_dir) project = Project(project_dir)
else: else:
@ -1999,34 +2001,13 @@ class Osc(cmdln.Cmdln):
if opts.headers: if opts.headers:
opts.headers = dict(opts.headers) opts.headers = dict(opts.headers)
try:
r = http_request(opts.method, r = http_request(opts.method,
url, url,
data=opts.data, data=opts.data,
file=opts.file, file=opts.file,
headers=opts.headers) headers=opts.headers)
except urllib2.HTTPError, e:
if e.code in [400, 404]:
print >>sys.stderr, e
print >>sys.stderr, e.read()
sys.exit(1)
elif e.code == 500:
print >>sys.stderr, e
# this may be unhelpful... because it may just print a big blob of uninteresting
# ichain html and javascript... however it could potentially be useful if the orign
# server returns an information body
if conf.config['http_debug']:
print >>sys.stderr, e.read()
sys.exit(1)
else:
sys.exit('unexpected error')
try:
out = r.read() out = r.read()
except:
sys.exit('failed to read from file object')
sys.stdout.write(out) sys.stdout.write(out)
@ -2060,7 +2041,7 @@ class Osc(cmdln.Cmdln):
prj = args[0] prj = args[0]
pac = args[1] pac = args[1]
else: else:
sys.exit('wrong argument count') raise oscerr.WrongArgs('I need at least one argument.')
maintainers = [] maintainers = []
@ -2107,8 +2088,7 @@ class Osc(cmdln.Cmdln):
args = slash_split(args) args = slash_split(args)
if len(args) != 3: if len(args) != 3:
print >>sys.stderr, 'error - incorrect number of arguments' raise oscerr.WrongArgs('Wrong number of arguments.')
sys.exit(1)
rev, dummy = parseRevisionOption(opts.revision) rev, dummy = parseRevisionOption(opts.revision)
import tempfile import tempfile

View File

@ -33,6 +33,7 @@ The configuration dictionary could look like this:
""" """
import ConfigParser import ConfigParser
from osc import oscerr
# being global to this module, this dict can be accessed from outside # being global to this module, this dict can be accessed from outside
# it will hold the parsed configuration # it will hold the parsed configuration
@ -55,12 +56,15 @@ DEFAULTS = { 'apisrv': 'https://api.opensuse.org/',
'%(scheme)s://%(apisrv)s/build/%(project)s/%(repository)s/%(buildarch)s/_repository/%(name)s', '%(scheme)s://%(apisrv)s/build/%(project)s/%(repository)s/%(buildarch)s/_repository/%(name)s',
], ],
'debug': '0',
'http_debug': '0', 'http_debug': '0',
'traceback': '0',
'post_mortem': '0',
'cookiejar': '~/.osc_cookiejar', 'cookiejar': '~/.osc_cookiejar',
# disable project tracking by default # disable project tracking by default
'do_package_tracking': '0', 'do_package_tracking': '0',
} }
boolean_opts = ['http_debug', 'do_package_tracking'] boolean_opts = ['debug', 'do_package_tracking', 'http_debug', 'post_mortem', 'traceback']
new_conf_template = """ new_conf_template = """
[general] [general]
@ -80,9 +84,18 @@ new_conf_template = """
# /srv/oscbuild/%%(repo)s-%%(arch)s # /srv/oscbuild/%%(repo)s-%%(arch)s
#build-root = %(build-root)s #build-root = %(build-root)s
# show info useful for debugging
#debug = 1
# show HTTP traffic useful for debugging # show HTTP traffic useful for debugging
#http_debug = 1 #http_debug = 1
# jump into the debugger in case of errors
#post_mortem = 1
# print call traces in case of errors
#traceback = 1
[%(apisrv)s] [%(apisrv)s]
user = %(user)s user = %(user)s
pass = %(pass)s pass = %(pass)s
@ -192,9 +205,42 @@ def get_configParser(conffile=None, force_read=False):
return get_configParser.cp return get_configParser.cp
def write_config(conffile, entries, custom_template = None, force = False):
"""
write osc's configuration file. entries is a dict which contains values
for the config file (e.g. { 'user' : 'username', 'pass' : 'password' } ).
custom_template is an optional configuration template. Use force=True if you
want to overwrite an existing configuration file.
"""
import os
conf_template = custom_template or new_conf_template
config = DEFAULTS.copy()
config.update(entries)
if force or not os.path.exists(conffile):
file = None
try:
file = open(conffile, 'w')
except IOError, e:
raise oscerr.OscIOError(e, 'cannot open configfile \'%s\'' % conffile)
try:
try:
os.chmod(conffile, 0600)
file.write(conf_template % config)
except IOError, e:
raise oscerr.OscIOError(e, 'cannot write configfile \'s\'' % conffile)
finally:
if file: file.close()
return True
else:
return False
def get_config(override_conffile = None, def get_config(override_conffile = None,
override_apisrv = None,
override_debug = None,
override_http_debug = None, override_http_debug = None,
override_apisrv = None): override_traceback = None,
override_post_mortem = None):
"""do the actual work (see module documentation)""" """do the actual work (see module documentation)"""
import os import os
import sys import sys
@ -223,23 +269,8 @@ def get_config(override_conffile = None,
netrc.netrc().authenticators(netrc_host) netrc.netrc().authenticators(netrc_host)
print >>sys.stderr, 'Read credentials from %s.' % os.path.expanduser('~/.netrc') print >>sys.stderr, 'Read credentials from %s.' % os.path.expanduser('~/.netrc')
except (IOError, TypeError, netrc.NetrcParseError): except (IOError, TypeError, netrc.NetrcParseError):
# raise oscerr.NoConfigfile(conffile, \
# last resort... ask the user account_not_configured_text % conffile)
#
import getpass
print >>sys.stderr, account_not_configured_text % conffile
config['user'] = raw_input('Username: ')
config['pass'] = getpass.getpass()
print >>sys.stderr, 'Creating osc configuration file %s ...' % conffile
fd = open(conffile, 'w')
os.chmod(conffile, 0600)
fd.write(new_conf_template % config)
fd.close()
print >>sys.stderr, 'done.'
#print >>sys.stderr, ('Now re-run the command.')
#sys.exit(0)
# okay, we made sure that .oscrc exists # okay, we made sure that .oscrc exists
@ -247,9 +278,9 @@ def get_config(override_conffile = None,
if not cp.has_section('general'): if not cp.has_section('general'):
# FIXME: it might be sufficient to just assume defaults? # FIXME: it might be sufficient to just assume defaults?
print >>sys.stderr, config_incomplete_text % conffile msg = config_incomplete_text % conffile
print >>sys.stderr, new_conf_template % DEFAULTS msg += new_conf_template % DEFAULTS
sys.exit(1) raise oscerr.ConfigError(msg)
config = dict(cp.items('general', raw=1)) config = dict(cp.items('general', raw=1))
@ -263,7 +294,7 @@ def get_config(override_conffile = None,
else: else:
config[i] = False config[i] = False
except: except:
sys.exit('option %s requires an integer value' % i) raise oscerr.ConfigError('option %s requires an integer value' % i)
packagecachedir = os.path.expanduser(config['packagecachedir']) packagecachedir = os.path.expanduser(config['packagecachedir'])
@ -283,8 +314,14 @@ def get_config(override_conffile = None,
config['auth_dict'] = auth_dict config['auth_dict'] = auth_dict
# override values which we were called with # override values which we were called with
if override_debug:
config['debug'] = override_debug
if override_http_debug: if override_http_debug:
config['http_debug'] = override_http_debug config['http_debug'] = override_http_debug
if override_traceback:
config['traceback'] = override_traceback
if override_post_mortem:
config['post_mortem'] = override_post_mortem
if override_apisrv: if override_apisrv:
config['scheme'], config['apisrv'] = \ config['scheme'], config['apisrv'] = \
parse_apisrv_url(config['scheme'], override_apisrv) parse_apisrv_url(config['scheme'], override_apisrv)

View File

@ -14,6 +14,7 @@ from urllib import pathname2url, quote_plus, urlencode
from urlparse import urlsplit, urlunsplit from urlparse import urlsplit, urlunsplit
from cStringIO import StringIO from cStringIO import StringIO
import shutil import shutil
import oscerr
import conf import conf
try: try:
from xml.etree import cElementTree as ET from xml.etree import cElementTree as ET
@ -252,8 +253,8 @@ class Project:
if conf.config['do_package_tracking'] and pac in self.pacs_unvers: if conf.config['do_package_tracking'] and pac in self.pacs_unvers:
# pac is not under version control but a local file/dir exists # pac is not under version control but a local file/dir exists
print 'can\'t add package \'%s\': Object already exists' % pac msg = 'can\'t add package \'%s\': Object already exists' % pac
sys.exit(1) raise oscerr.PackageExists(self.name, pac, msg)
else: else:
print 'checking out new package %s' % pac print 'checking out new package %s' % pac
checkout_package(self.apiurl, self.name, pac, \ checkout_package(self.apiurl, self.name, pac, \
@ -318,7 +319,7 @@ class Project:
self.new_package_entry(pac, 'A') self.new_package_entry(pac, 'A')
self.write_packages() self.write_packages()
# sometimes the new pac doesn't exist in the list because # sometimes the new pac doesn't exist in the list because
# it would take too much time to update all data structs regulary # it would take too much time to update all data structs regularly
if pac in self.pacs_unvers: if pac in self.pacs_unvers:
self.pacs_unvers.remove(pac) self.pacs_unvers.remove(pac)
return True return True
@ -345,9 +346,7 @@ class Project:
pac.delete_storefile(file) pac.delete_storefile(file)
# this is not really necessary # this is not really necessary
pac.put_on_deletelist(file) pac.put_on_deletelist(file)
print statfrmt('D', os.path.join(pac.dir, file)) print statfrmt('D', getTransActPath(os.path.join(pac.dir, file)))
#print os.path.dirname(pac.dir)
# some black path vodoo
print statfrmt('D', getTransActPath(os.path.join(pac.dir, os.pardir, pac.name))) print statfrmt('D', getTransActPath(os.path.join(pac.dir, os.pardir, pac.name)))
pac.write_deletelist() pac.write_deletelist()
self.set_state(pac.name, 'D') self.set_state(pac.name, 'D')
@ -372,6 +371,9 @@ class Project:
for pac in pacs: for pac in pacs:
Package(os.path.join(self.dir, pac)).update() Package(os.path.join(self.dir, pac)).update()
else: else:
# we need to make sure that the _packages file will be written (even if an exception
# occurs)
try:
# update complete project # update complete project
# packages which no longer exists upstream # packages which no longer exists upstream
upstream_del = [ pac for pac in self.pacs_have if not pac in self.pacs_available and self.get_state(pac) != 'A'] upstream_del = [ pac for pac in self.pacs_have if not pac in self.pacs_available and self.get_state(pac) != 'A']
@ -416,8 +418,8 @@ class Project:
Package(os.path.join(self.dir, pac)).update() Package(os.path.join(self.dir, pac)).update()
elif state == 'A' and pac in self.pacs_available: elif state == 'A' and pac in self.pacs_available:
# file/dir called pac already exists and is under version control # file/dir called pac already exists and is under version control
print 'can\'t add package \'%s\': Object already exists' % pac msg = 'can\'t add package \'%s\': Object already exists' % pac
sys.exit(1) raise oscerr.PackageExists(self.name, pac, msg)
elif state == 'A': elif state == 'A':
# do nothing # do nothing
pass pass
@ -425,10 +427,12 @@ class Project:
print 'unexpected state.. package \'%s\'' % pac print 'unexpected state.. package \'%s\'' % pac
self.checkout_missing_pacs() self.checkout_missing_pacs()
finally:
self.write_packages() self.write_packages()
def commit(self, pacs = (), msg = '', files = {}): def commit(self, pacs = (), msg = '', files = {}):
if len(pacs): if len(pacs):
try:
for pac in pacs: for pac in pacs:
todo = [] todo = []
if files.has_key(pac): if files.has_key(pac):
@ -452,24 +456,25 @@ class Project:
print 'osc: \'%s\' package not found' % pac print 'osc: \'%s\' package not found' % pac
elif state == None: elif state == None:
self.commitExtPackage(pac, msg, todo) self.commitExtPackage(pac, msg, todo)
finally:
self.write_packages()
else: else:
# if we have packages marked as '!' we cannot commit # if we have packages marked as '!' we cannot commit
for pac in self.pacs_broken: for pac in self.pacs_broken:
if self.get_state(pac) != 'D': if self.get_state(pac) != 'D':
print 'commit failed: package \'%s\' is missing' % pac msg = 'commit failed: package \'%s\' is missing' % pac
sys.exit(1) raise oscerr.PackageMissing(self.name, pac, msg)
try:
for pac in self.pacs_have: for pac in self.pacs_have:
state = self.get_state(pac) state = self.get_state(pac)
if state == ' ': if state == ' ':
# do a simple commit # do a simple commit
try:
Package(os.path.join(self.dir, pac)).commit(msg) Package(os.path.join(self.dir, pac)).commit(msg)
except SystemExit:
pass
elif state == 'D': elif state == 'D':
self.commitDelPackage(pac) self.commitDelPackage(pac)
elif state == 'A': elif state == 'A':
self.commitNewPackage(pac, msg) self.commitNewPackage(pac, msg)
finally:
self.write_packages() self.write_packages()
def commitNewPackage(self, pac, msg = '', files = []): def commitNewPackage(self, pac, msg = '', files = []):
@ -677,10 +682,7 @@ class Package:
else: else:
upstream_rev = show_upstream_rev(self.apiurl, self.prjname, self.name) upstream_rev = show_upstream_rev(self.apiurl, self.prjname, self.name)
if self.rev != upstream_rev: if self.rev != upstream_rev:
print >>sys.stderr, 'Working copy \'%s\' is out of date (rev %s vs rev %s).' \ raise oscerr.WorkingCopyOutdated((self.absdir, self.rev, upstream_rev))
% (self.absdir, self.rev, upstream_rev)
print >>sys.stderr, 'Looks as if you need to update it first.'
sys.exit(1)
if not self.todo: if not self.todo:
self.todo = self.filenamelist_unvers + self.filenamelist self.todo = self.filenamelist_unvers + self.filenamelist
@ -1247,6 +1249,8 @@ def slash_split(l):
def findpacs(files): def findpacs(files):
"""collect Package objects belonging to the given files
and make sure each Package is returned only once"""
pacs = [] pacs = []
for f in files: for f in files:
p = filedir_to_pac(f) p = filedir_to_pac(f)
@ -1287,6 +1291,10 @@ def read_inconflict(dir):
def parseargs(list_of_args): def parseargs(list_of_args):
"""Convenience method osc's commandline argument parsing.
If called with an empty tuple (or list), return a list containing the current directory.
Otherwise, return a list of the arguments."""
if list_of_args: if list_of_args:
return list(list_of_args) return list(list_of_args)
else: else:
@ -1294,6 +1302,10 @@ def parseargs(list_of_args):
def filedir_to_pac(f): def filedir_to_pac(f):
"""Takes a working copy path, or a path to a file inside a working copy,
and returns a Package object instance
If the argument was a filename, add it onto the "todo" list of the Package """
if os.path.isdir(f): if os.path.isdir(f):
wd = f wd = f
@ -1385,6 +1397,8 @@ def http_request(method, url, headers={}, data=None, file=None):
else: else:
raise raise
if conf.config['debug']: print method, url
try: try:
fd = urllib2.urlopen(req, data=data) fd = urllib2.urlopen(req, data=data)
@ -1453,8 +1467,8 @@ def check_store_version(dir):
v = '' v = ''
if v == '': if v == '':
print >>sys.stderr, 'error: "%s" is not an osc working copy' % dir msg = 'Error: "%s" is not an osc working copy.' % os.path.abspath(dir)
sys.exit(1) raise oscerr.NoWorkingCopy(msg)
if v != __version__: if v != __version__:
if v in ['0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '0.95', '0.96', '0.97', '0.98']: if v in ['0.2', '0.3', '0.4', '0.5', '0.6', '0.7', '0.8', '0.9', '0.95', '0.96', '0.97', '0.98']:
@ -1463,12 +1477,10 @@ def check_store_version(dir):
f.write(__version__ + '\n') f.write(__version__ + '\n')
f.close() f.close()
return return
print >>sys.stderr msg = 'The osc metadata of your working copy "%s"' % dir
print >>sys.stderr, 'the osc metadata of your working copy "%s"' % dir msg += '\nhas the wrong version (%s), should be %s' % (v, __version__)
print >>sys.stderr, 'has the wrong version (%s), should be %s' % (v, __version__) msg += '\nPlease do a fresh checkout'
print >>sys.stderr, 'please do a fresh checkout' raise oscerr.WorkingCopyWrongVersion, msg
print >>sys.stderr
sys.exit(1)
def meta_get_packagelist(apiurl, prj): def meta_get_packagelist(apiurl, prj):
@ -1523,22 +1535,23 @@ def show_project_conf(apiurl, prj):
def show_package_meta(apiurl, prj, pac): def show_package_meta(apiurl, prj, pac):
try:
url = makeurl(apiurl, ['source', prj, pac, '_meta']) url = makeurl(apiurl, ['source', prj, pac, '_meta'])
try:
f = http_GET(url) f = http_GET(url)
except urllib2.HTTPError, e:
print >>sys.stderr, 'error getting meta for project \'%s\' package \'%s\'' % (prj, pac)
print >>sys.stderr, e
if e.code == 500:
print >>sys.stderr, '\nDebugging output follows.\nurl:\n%s\nresponse:\n%s' % (url, e.read())
sys.exit(1)
return f.readlines() return f.readlines()
except urllib2.HTTPError, e:
e.osc_msg = 'Error getting meta for project \'%s\' package \'%s\'' % (prj, pac)
raise
def show_pattern_metalist(apiurl, prj): def show_pattern_metalist(apiurl, prj):
url = makeurl(apiurl, ['source', prj, '_pattern']) url = makeurl(apiurl, ['source', prj, '_pattern'])
try:
f = http_GET(url) f = http_GET(url)
tree = ET.parse(f) tree = ET.parse(f)
except urllib2.HTTPError, e:
e.osc_msg = 'show_pattern_metalist: Error getting pattern list for project \'%s\'' % prj
raise
r = [ node.get('name') for node in tree.getroot() ] r = [ node.get('name') for node in tree.getroot() ]
r.sort() r.sort()
return r return r
@ -1548,11 +1561,10 @@ def show_pattern_meta(apiurl, prj, pattern):
url = makeurl(apiurl, ['source', prj, '_pattern', pattern]) url = makeurl(apiurl, ['source', prj, '_pattern', pattern])
try: try:
f = http_GET(url) f = http_GET(url)
except urllib2.HTTPError, e:
print >>sys.stderr, 'error getting pattern \'%s\' for project \'%s\'' % (pattern, prj)
print >>sys.stderr, e
sys.exit(1)
return f.readlines() return f.readlines()
except urllib2.HTTPError, e:
e.osc_msg = 'show_pattern_meta: Error getting pattern \'%s\' for project \'%s\'' % (pattern, prj)
raise
class metafile: class metafile:
@ -1650,7 +1662,7 @@ def make_meta_url(metatype, path_args=None, apiurl=None):
if not apiurl: if not apiurl:
apiurl = conf.config['apiurl'] apiurl = conf.config['apiurl']
if metatype not in metatypes.keys(): if metatype not in metatypes.keys():
sys.exit('unknown metatype %s' % metatype) raise AttributeError('make_meta_url(): Unknown meta type \'%s\'' % metatype)
path = metatypes[metatype]['path'] path = metatypes[metatype]['path']
if path_args: if path_args:
@ -1742,8 +1754,8 @@ def read_meta_from_spec(specfile, *args):
""" """
if not os.path.isfile(specfile): if not os.path.isfile(specfile):
print 'file \'%s\' is not a readable file' % specfile msg = 'File \'%s\' is not a readable file' % specfile
sys.exit(1) raise oscerr.UnreadableFile, msg
try: try:
lines = codecs.open(specfile, 'r', locale.getpreferredencoding()).readlines() lines = codecs.open(specfile, 'r', locale.getpreferredencoding()).readlines()
@ -2192,10 +2204,9 @@ def checkout_package(apiurl, project, package,
if not pathname: if not pathname:
pathname = getTransActPath(os.path.join(prj_dir, package)) pathname = getTransActPath(os.path.join(prj_dir, package))
path = (quote_plus(project), quote_plus(package)) # before we create directories and stuff, check if the package actually
if meta_exists(metatype='pkg', path_args=path, create_new=False, apiurl=apiurl) == None: # exists
print >>sys.stderr, 'error 404 - project or package does not exist' show_package_meta(apiurl, project, package)
sys.exit(1)
if expand_link: if expand_link:
# try to read from the linkinfo # try to read from the linkinfo
@ -2348,30 +2359,12 @@ def copy_pac(src_apiurl, src_project, src_package,
def delete_package(apiurl, prj, pac): def delete_package(apiurl, prj, pac):
u = makeurl(apiurl, ['source', prj, pac]) u = makeurl(apiurl, ['source', prj, pac])
try:
http_DELETE(u) http_DELETE(u)
except urllib2.HTTPError, e:
if e.code == 404:
print >>sys.stderr, 'Package \'%s\' does not exist' % pac
sys.exit(1)
else:
print >>sys.stderr, 'an unexpected error occured while deleting ' \
'\'%s\'' % pac
sys.exit(1)
def delete_project(apiurl, prj): def delete_project(apiurl, prj):
u = makeurl(apiurl, ['source', prj]) u = makeurl(apiurl, ['source', prj])
try:
http_DELETE(u) http_DELETE(u)
except urllib2.HTTPError, e:
if e.code == 404:
print >>sys.stderr, 'Package \'%s\' does not exist' % pac
sys.exit(1)
else:
print >>sys.stderr, 'an unexpected error occured while deleting ' \
'\'%s\'' % pac
sys.exit(1)
def get_platforms(apiurl): def get_platforms(apiurl):
@ -2660,10 +2653,8 @@ def rebuild(apiurl, prj, package, repo, arch, code=None):
try: try:
f = http_POST(u) f = http_POST(u)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
print >>sys.stderr, 'could not trigger rebuild for project \'%s\' package \'%s\'' % (prj, package) e.osc_msg = 'could not trigger rebuild for project \'%s\' package \'%s\'' % (prj, package)
print >>sys.stderr, u raise
print >>sys.stderr, e
sys.exit(1)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
return root.get('code') return root.get('code')
@ -2673,9 +2664,8 @@ def store_read_project(dir):
try: try:
p = open(os.path.join(dir, store, '_project')).readlines()[0].strip() p = open(os.path.join(dir, store, '_project')).readlines()[0].strip()
except IOError: except IOError:
print >>sys.stderr, 'error: \'%s\' is not an osc project dir ' \ raise oscerr.NoWorkingCopy('Error: \'%s\' is not an osc project dir ' \
'or working copy' % dir 'or working copy.' % os.path.abspath(dir))
sys.exit(1)
return p return p
@ -2683,8 +2673,8 @@ def store_read_package(dir):
try: try:
p = open(os.path.join(dir, store, '_package')).readlines()[0].strip() p = open(os.path.join(dir, store, '_package')).readlines()[0].strip()
except IOError: except IOError:
print >>sys.stderr, 'error: \'%s\' is not an osc working copy' % dir raise oscerr.NoWorkingCopy('error: \'%s\' is not an osc working copy' \
sys.exit(1) % os.path.abspath(dir))
return p return p
def store_read_apiurl(dir): def store_read_apiurl(dir):
@ -2727,17 +2717,15 @@ def abortbuild(apiurl, project, package=None, arch=None, repo=None):
try: try:
f = http_POST(u) f = http_POST(u)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
err_str = 'abortion failed for project %s' % project e.osc_msg = 'abortion failed for project %s' % project
if package: if package:
err_str += ' package %s' % package e.osc_msg += ' package %s' % package
if arch: if arch:
err_str += ' arch %s' % arch e.osc_msg += ' arch %s' % arch
if repo: if repo:
err_str += ' repo %s' % repo e.osc_msg += ' repo %s' % repo
print >> sys.stderr, err_str raise
print >> sys.stderr, u
print >> sys.stderr, e
sys.exit(1)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
return root.get('code') return root.get('code')
@ -2757,19 +2745,17 @@ def wipebinaries(apiurl, project, package=None, arch=None, repo=None, code=None)
try: try:
f = http_POST(u) f = http_POST(u)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
err_str = 'wipe binary rpms failed for project %s' % project e.osc_msg = 'wipe binary rpms failed for project %s' % project
if package: if package:
err_str += ' package %s' % package e.osc_msg += ' package %s' % package
if arch: if arch:
err_str += ' arch %s' % arch e.osc_msg += ' arch %s' % arch
if repo: if repo:
err_str += ' repository %s' % repo e.osc_msg += ' repository %s' % repo
if code: if code:
err_str += ' code=%s' % code e.osc_msg += ' code=%s' % code
print >> sys.stderr, err_str raise
print >> sys.stderr, u
print >> sys.stderr, e
sys.exit(1)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
return root.get('code') return root.get('code')
@ -3061,22 +3047,6 @@ def is_srcrpm(f):
else: else:
return False return False
def delete_server_files(apiurl, prj, pac, files):
"""
This method deletes the given filelist on the
server. No local data will be touched.
"""
for file in files:
try:
u = makeurl(apiurl, ['source', prj, pac, file])
http_DELETE(u)
except:
# we do not handle all exceptions here - we need another solution
# see bug #280034
print >>sys.stderr, 'error while deleting file \'%s\'' % file
sys.exit(1)
def addMaintainer(apiurl, prj, pac, user): def addMaintainer(apiurl, prj, pac, user):
""" add a new maintainer to a package or project """ """ add a new maintainer to a package or project """
path = quote_plus(prj), path = quote_plus(prj),

91
osc/oscerr.py Normal file
View File

@ -0,0 +1,91 @@
#!/usr/bin/python
# Copyright (C) 2008 Peter Poeml / Novell Inc. All rights reserved.
# This program is free software; it may be used, copied, modified
# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
class OscBaseError(Exception):
def __init__(self, args=()):
Exception.__init__(self)
self.args = args
class ConfigError(OscBaseError):
"""Exception raised when there is an error in the config file"""
def __init__(self, msg):
OscBaseError.__init__(self)
self.msg = msg
class NoConfigfile(OscBaseError):
"""Exception raised when osc's configfile cannot be found"""
def __init__(self, fname, msg):
OscBaseError.__init__(self)
self.file = fname
self.msg = msg
class WrongArgs(OscBaseError):
"""Exception raised by the cli for wrong arguments usage"""
class WrongOptions(OscBaseError):
"""Exception raised by the cli for wrong option usage"""
#def __str__(self):
# s = 'Sorry, wrong options.'
# if self.args:
# s += '\n' + self.args
# return s
class NoWorkingCopy(OscBaseError):
"""Exception raised when directory is neither a project dir nor a package dir"""
class WorkingCopyWrongVersion(OscBaseError):
"""Exception raised when working copy's .osc/_osclib_version doesn't match"""
class WorkingCopyOutdated(OscBaseError):
"""Exception raised when the working copy is outdated.
It takes a tuple with three arguments: path to wc,
revision that it has, revision that it should have.
"""
def __str__(self):
return ('Working copy \'%s\' is out of date (rev %s vs rev %s).\n'
'Looks as if you need to update it first.' \
% (self[0], self[1], self[2]))
class UnreadableFile(OscBaseError):
def __init__(self, msg):
OscBaseError.__init__(self)
self.msg = msg
class OscIOError(OscBaseError):
def __init__(self, e, msg):
OscBaseError.__init__(self)
self.e = e
self.msg = msg
class SignalInterrupt(Exception):
"""Exception raised on SIGTERM and SIGHUP."""
class PackageError(OscBaseError):
"""Base class for all Package related exceptions"""
def __init__(self, prj, pac):
OscBaseError.__init__(self)
self.prj = prj
self.pac = pac
class PackageExists(PackageError):
"""
Exception raised when a local object already exists
"""
def __init__(self, prj, pac, msg):
PackageError.__init__(self, prj, pac)
self.msg = msg
class PackageMissing(PackageError):
"""
Exception raised when a local object doesn't exist
"""
def __init__(self, prj, pac, msg):
PackageError.__init__(self, prj, pac)
self.msg = msg