2020import signal
2121import sys
2222import time
23- import traceback
2423import unittest
2524import uuid
2625from copy import deepcopy
2726from datetime import datetime
28- from logging import DEBUG
2927from typing import TYPE_CHECKING , Optional
3028
3129from rich .console import Console
4543from rucio .common .config import config_get , config_get_float
4644from rucio .common .constants import ReplicaState
4745from rucio .common .exception import (
48- DIDFilterSyntaxError ,
49- DuplicateCriteriaInDIDFilter ,
5046 DuplicateRule ,
5147 InputValidationError ,
5248 InvalidObject ,
5349 InvalidType ,
50+ RSENotFound ,
5451 RucioException ,
52+ ScopeNotFound ,
5553 UnsupportedOperation ,
5654)
5755from rucio .common .extra import import_extras
@@ -109,8 +107,7 @@ def ping(args, client, logger, console, spinner):
109107 if server_info :
110108 print (server_info ['version' ])
111109 return SUCCESS
112- logger .error ('Ping failed' )
113- return FAILURE
110+ raise RucioException ('Ping failed' )
114111
115112
116113@exception_handler
@@ -232,11 +229,9 @@ def list_file_replicas(args, client, logger, console, spinner):
232229 table_data = []
233230 dids = []
234231 if args .missing and not args .rses :
235- print ('Cannot use --missing without specifying a RSE' )
236- return FAILURE
232+ raise InputValidationError ('Cannot use --missing without specifying a RSE' )
237233 if args .link and ':' not in args .link :
238- print ('The substitution parameter must equal --link="/pfn/dir:/dst/dir"' )
239- return FAILURE
234+ raise ValueError ('The substitution parameter must equal --link="/pfn/dir:/dst/dir"' )
240235
241236 if cli_config == 'rich' :
242237 spinner .update (status = 'Fetching file replicas' )
@@ -398,14 +393,13 @@ def attach(args, client, logger, console, spinner):
398393
399394 if args .fromfile :
400395 if len (dids ) > 1 :
401- logger .error ("If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line." )
402- return FAILURE
396+ raise ValueError ('If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line.' )
403397 try :
404398 f = open (dids [0 ], 'r' )
405399 dids = [did .rstrip () for did in f .readlines ()]
406- except OSError :
400+ except OSError as error :
407401 logger .error ("Can't open file '" + dids [0 ] + "'." )
408- return FAILURE
402+ raise OSError from error
409403
410404 dids = [{'scope' : get_scope (did , client )[0 ], 'name' : get_scope (did , client )[1 ]} for did in dids ]
411405 if len (dids ) <= limit :
@@ -468,35 +462,16 @@ def list_dids(args, client, logger, console, spinner):
468462 name = '*'
469463
470464 if scope not in client .list_scopes ():
471- logger .error ('Scope not found.' )
472- return FAILURE
465+ raise ScopeNotFound
473466
474467 if args .recursive and '*' in name :
475- logger .error ('Option recursive cannot be used with wildcards.' )
476- return FAILURE
468+ raise InputValidationError ('Option recursive cannot be used with wildcards.' )
477469 else :
478470 if filters :
479471 if ('name' in filters ) and (name != '*' ):
480- logger .error ('Must have a wildcard in did name if filtering by name.' )
481- return FAILURE
472+ raise ValueError ('Must have a wildcard in did name if filtering by name.' )
482473
483- try :
484- filters , type_ = parse_did_filter_from_string_fe (args .filter , name )
485- except InvalidType as error :
486- logger .error (error )
487- return FAILURE
488- except DuplicateCriteriaInDIDFilter as error :
489- logger .error (error )
490- return FAILURE
491- except DIDFilterSyntaxError as error :
492- logger .error (error )
493- return FAILURE
494- except ValueError as error :
495- logger .error (error )
496- return FAILURE
497- except Exception as e :
498- logger .error (e )
499- return FAILURE
474+ filters , type_ = parse_did_filter_from_string_fe (args .filter , name )
500475
501476 if cli_config == 'rich' :
502477 spinner .update (status = 'Fetching DIDs' )
@@ -531,8 +506,7 @@ def list_dids_extended(args, client, logger, console, spinner):
531506
532507 List the data identifiers for a given scope (DEPRECATED).
533508 """
534- logger .error ("This command has been deprecated. Please use list_dids instead." )
535- return FAILURE
509+ raise UnsupportedOperation ('This command has been deprecated. Please use list_dids instead.' )
536510
537511
538512@exception_handler
@@ -813,8 +787,7 @@ def list_parent_dids(args, client, logger, console, spinner):
813787 else :
814788 print (tabulate (table_data , tablefmt = tablefmt , headers = ['SCOPE:NAME' , '[DID TYPE]' ]))
815789 else :
816- print ('At least one option has to be given. Use -h to list the options.' )
817- return FAILURE
790+ raise InputValidationError ('At least one option has to be given. Use -h to list the options.' )
818791 return SUCCESS
819792
820793
@@ -931,13 +904,11 @@ def upload(args, client, logger, console, spinner):
931904 Upload files into Rucio
932905 """
933906 if args .lifetime and args .expiration_date :
934- logger .error ("--lifetime and --expiration-date cannot be specified at the same time." )
935- return FAILURE
907+ raise InputValidationError ("--lifetime and --expiration-date cannot be specified at the same time." )
936908 elif args .expiration_date :
937909 expiration_date = datetime .strptime (args .expiration_date , "%Y-%m-%d-%H:%M:%S" )
938910 if expiration_date < datetime .utcnow ():
939- logger .error ("The specified expiration date should be in the future!" )
940- return FAILURE
911+ raise ValueError ("The specified expiration date should be in the future!" )
941912 args .lifetime = (expiration_date - datetime .utcnow ()).total_seconds ()
942913
943914 dsscope = None
@@ -1023,23 +994,18 @@ def download(args, client, logger, console, spinner):
1023994 """
1024995 # Input validation
1025996 if not args .dids and not args .filter and not args .metalink_file :
1026- logger .error ('At least one did is mandatory' )
1027- return FAILURE
997+ raise InputValidationError ('At least one did is mandatory' )
1028998 elif not args .dids and args .filter and not args .scope :
1029- logger .error ('The argument scope is mandatory' )
1030- return FAILURE
999+ raise InputValidationError ('The argument scope is mandatory' )
10311000
10321001 if args .filter and args .metalink_file :
1033- logger .error ('Arguments filter and metalink cannot be used together.' )
1034- return FAILURE
1002+ raise InputValidationError ('Arguments filter and metalink cannot be used together.' )
10351003
10361004 if args .dids and args .metalink_file :
1037- logger .error ('Arguments dids and metalink cannot be used together.' )
1038- return FAILURE
1005+ raise InputValidationError ('Arguments dids and metalink cannot be used together.' )
10391006
10401007 if args .ignore_checksum and args .check_local_with_filesize_only :
1041- logger .error ('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.' )
1042- return FAILURE
1008+ raise InputValidationError ('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.' )
10431009
10441010 trace_pattern = {}
10451011
@@ -1083,16 +1049,12 @@ def download(args, client, logger, console, spinner):
10831049 filters , type_ = parse_did_filter_from_string (args .filter )
10841050 if args .scope :
10851051 filters ['scope' ] = args .scope
1086- except InvalidType as error :
1087- logger .error (error )
1088- return FAILURE
1089- except ValueError as error :
1052+ except (InvalidType , ValueError ) as error :
10901053 logger .error (error )
1091- return FAILURE
1054+ raise error
10921055 except Exception as error :
1093- logger .error (error )
10941056 logger .error ("Invalid Filter. Filter must be 'key=value', 'key>=value', 'key>value', 'key<=value', 'key<value'" )
1095- return FAILURE
1057+ raise error
10961058 item_defaults ['filters' ] = filters
10971059
10981060 if not args .pfn :
@@ -1148,8 +1110,7 @@ def download(args, client, logger, console, spinner):
11481110
11491111 download_rse = _get_rse_for_pfn (replicas , args .pfn )
11501112 if download_rse is None :
1151- logger .error ("Could not find RSE for pfn %s" , args .pfn )
1152- return FAILURE
1113+ raise RSENotFound ("Could not find RSE for pfn %s" % args .pfn )
11531114 else :
11541115 item_defaults ['rse' ] = download_rse
11551116
@@ -1354,8 +1315,7 @@ def delete_rule(args, client, logger, console, spinner):
13541315 except ValueError :
13551316 # Otherwise, trying to extract the scope, name from args.rule_id
13561317 if not args .rses :
1357- logger .error ('A RSE expression must be specified if you do not provide a rule_id but a DID' )
1358- return FAILURE
1318+ raise InputValidationError ('A RSE expression must be specified if you do not provide a rule_id but a DID' )
13591319 scope , name = get_scope (args .rule_id , client )
13601320 rules = client .list_did_rules (scope = scope , name = name )
13611321 if args .rule_account is None :
@@ -1372,8 +1332,7 @@ def delete_rule(args, client, logger, console, spinner):
13721332 client .delete_replication_rule (rule_id = rule ['id' ], purge_replicas = args .purge_replicas )
13731333 deletion_success = True
13741334 if not deletion_success :
1375- logger .error ('No replication rule was deleted from the DID' )
1376- return FAILURE
1335+ raise RucioException ('No replication rule was deleted from the DID' )
13771336 return SUCCESS
13781337
13791338
@@ -1394,8 +1353,8 @@ def update_rule(args, client, logger, console, spinner):
13941353 elif args .locked .title () == "False" :
13951354 options ['locked' ] = False
13961355 else :
1397- logger . error ('Locked must be True or False' )
1398- return FAILURE
1356+ raise InputValidationError ('Locked must be True or False' )
1357+
13991358 if args .comment :
14001359 options ['comment' ] = args .comment
14011360 if args .rule_account :
@@ -1410,8 +1369,7 @@ def update_rule(args, client, logger, console, spinner):
14101369 options ['source_replica_expression' ] = None if args .source_replica_expression .lower () == 'none' else args .source_replica_expression
14111370 if args .cancel_requests :
14121371 if 'state' not in options :
1413- logger .error ('--stuck or --suspend must be specified when running --cancel-requests' )
1414- return FAILURE
1372+ raise InputValidationError ('--stuck or --suspend must be specified when running --cancel-requests' )
14151373 options ['cancel_requests' ] = True
14161374 if args .priority :
14171375 options ['priority' ] = int (args .priority )
@@ -1582,8 +1540,7 @@ def list_rules(args, client, logger, console, spinner):
15821540 name = args .subscription [1 ]
15831541 rules = client .list_subscription_rules (account = account , name = name )
15841542 else :
1585- print ('At least one option has to be given. Use -h to list the options.' )
1586- return FAILURE
1543+ raise InputValidationError ('At least one option has to be given. Use -h to list the options.' )
15871544 if args .csv :
15881545 for rule in rules :
15891546 print (rule ['id' ],
@@ -1990,21 +1947,18 @@ def add_lifetime_exception(args, client, logger, console, spinner):
19901947 """
19911948
19921949 if not args .reason :
1993- logger .error ('reason for the extension is mandatory' )
1994- return FAILURE
1950+ raise InputValidationError ('reason for the extension is mandatory' )
19951951 reason = args .reason
19961952 if not args .expiration :
1997- logger .error ('expiration is mandatory' )
1998- return FAILURE
1953+ raise InputValidationError ('expiration is mandatory' )
19991954 try :
20001955 expiration = datetime .strptime (args .expiration , "%Y-%m-%d" )
20011956 except Exception as err :
2002- logger . error ( err )
2003- return FAILURE
1957+ msg = f'Cannot parse expiration date: { err } '
1958+ raise ValueError ( msg )
20041959
20051960 if not args .inputfile :
2006- logger .error ('inputfile is mandatory' )
2007- return FAILURE
1961+ raise InputValidationError ('inputfile is mandatory' )
20081962 with open (args .inputfile ) as infile :
20091963 # Deduplicate the content of the input file and ignore empty lines.
20101964 dids = set (did for line in infile if (did := line .strip ()))
@@ -2067,22 +2021,8 @@ def add_lifetime_exception(args, client, logger, console, spinner):
20672021 if not datasets :
20682022 logger .error ('Nothing to submit' )
20692023 return SUCCESS
2070- try :
2071- client .add_exception (dids = datasets , account = client .account , pattern = '' , comments = reason , expires_at = expiration )
2072- except UnsupportedOperation as err :
2073- logger .error (err )
2074- return FAILURE
2075- except Exception :
2076- error_message = 'Failure to submit exception. Please retry.'
2077- if cli_config == 'rich' :
2078- if logger .level == DEBUG :
2079- logger .exception (error_message )
2080- else :
2081- logger .error (error_message )
2082- else :
2083- logger .error (error_message )
2084- logger .debug (traceback .format_exc ())
2085- return FAILURE
2024+
2025+ client .add_exception (dids = datasets , account = client .account , pattern = '' , comments = reason , expires_at = expiration )
20862026
20872027 logger .info ('Exception successfully submitted. Summary below:' )
20882028 for key , data in error_summary .items ():
0 commit comments