9 By inheriting from
this module you make your module able to deal with parameters:
11 1) parsing of parameters in the order of precedence, starting with the lowest:
14 # $self->param_init( $lowest_precedence_hashref, $middle_precedence_hashref, $highest_precedence_hashref );
18 # $runObj->param_defaults(), # module-wide built-in defaults have the lowest precedence (will always be the same for this module)
19 # $hive_pipeline->params_as_hash(), # then come the pipeline-wide parameters from the 'pipeline_wide_parameters' table (define things common to all analyses in this pipeline)
20 # $self->analysis->parameters(), # analysis-wide 'parameters' are even more specific (can be defined differently for several occurence of the same module)
21 # $job->input_id(), # job-specific 'input_id' parameters have the highest precedence
22 # $job->accu_hash(), # parameters accumulated and sent for this job by other preceding jobs
26 2) reading a parameter
's value
28 # my $source = $self->param('source
'); )
30 3) dynamically setting a parameter's value
32 # $self->param(
'binpath',
'/software/ensembl/compara');
34 Note: It proved to be a convenient mechanism to exchange params
35 between fetch_input(),
run(), write_output() and other methods.
39 Most of Compara RunnableDB methods work under assumption
40 that both analysis.parameters and job.input_id fields contain a Perl-style parameter hashref as a
string.
42 This module implements a generic param() method that allows to set parameters according to the following parameter precedence rules:
44 (1) Job-Specific parameters defined in job.input_id hash, they have the highest priority and override everything else.
46 (2) Analysis-Wide parameters defined in analysis.parameters hash. Can be overridden by (1).
48 (3) Pipeline-Wide parameters defined in the 'meta' table. Can be overridden by (1) and (2).
50 (4) Module_Defaults that are hard-coded into modules have the lowest precedence. Can be overridden by (1), (2) and (3).
53 param_exists() returns 1 if the parameter is present and can be substituted,
54 undef if the substitution failed.
55 0 if the parameter is absent,
56 param_is_defined() returns 1 if the parameter is present and can be substituted to a defined value,
57 undef if the substitution fails,
59 param() returns the value if param_exists() returned true, undef otherwise.
60 param_required() is like param() but dies instead of returning undef.
62 In practice. given this hash of parameters:
68 the Params API would
return:
71 -------------------+----------------------------
72 param_exists() | 1 1 undef 0
73 param_is_defined() | 1 0 undef 0
74 param() | 3 undef undef undef
75 param_required() | 3 (die) (die) (die)
80 Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
81 Copyright [2016-2024] EMBL-European Bioinformatics Institute
83 Licensed under the Apache License,
Version 2.0 (the
"License"); you may not use
this file except in compliance with the License.
84 You may obtain a copy of the License at
88 Unless required by applicable law or agreed to in writing, software distributed under the License
89 is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90 See the License
for the specific language governing permissions and limitations under the License.
94 Please subscribe to the
Hive mailing list: http:
99 package Bio::EnsEMBL::Hive::Params;
104 use List::Util qw(first min max minstr maxstr reduce sum shuffle); # make them available
for substituted expressions
105 use
Bio::EnsEMBL::Hive::Utils (
'stringify',
'dir_revhash',
'go_figure_dbc',
'throw'); # NB: dir_revhash() is used by some substituted expressions,
do not remove!
110 Description: a trivial constructor, mostly
for testing a
Params object
115 my $class = shift @_;
117 return bless {}, $class;
121 =head2 fuse_param_hashes
123 Description: Performs the actual task of evaluating and fusing/merging a preference list of parameter hashes into one parameter hash.
127 sub fuse_param_hashes {
128 my $self = shift @_; # NB: other parameters will be shifted off it
132 foreach my $source (@_) {
133 if(ref($source) ne
'HASH') {
135 $param_hash = eval($source)
if(defined($source));
136 $param_hash = {}
if(!defined($param_hash));
139 die
"Could not evaluate '$source': $@\n";
140 } elsif(ref($param_hash) ne
'HASH') {
141 die
"Expected a {'param'=>'value'} hashref, but got the following string instead: '$source'\n";
143 $source = $param_hash;
145 while(my ($k,$v) = each %$source ) {
146 $fused_hash{$k} = $v;
156 Description: Sets up the unsubstituted parameters in the right precedence order (called by AnalysisJob::load_parameters)
161 my $self = shift @_; # NB: other parameters will be shifted off it
164 $self->{
'_param_hash'} = {};
168 sub _param_possibly_overridden {
169 my ($self, $param_name, $overriding_hash) = @_;
171 return ( ( (ref($overriding_hash) eq
'HASH') && exists($overriding_hash->{ $param_name }) )
172 ? $overriding_hash->{ $param_name }
173 : $self->_param_silent($param_name)
180 my $param_name = shift @_
181 or
throw(
"ParamError: calling param() without arguments");
183 if(@_) { # If there is a value (even
if undef), then set it!
184 my $new_val = shift @_;
185 if (@_ and (shift)) {
186 # If there is an extra parameter after the value, it means that
187 # the value is unsubstituted
188 $self->{
'_unsubstituted_param_hash'}{$param_name} = $new_val;
190 $self->{
'_param_hash'}{$param_name} = $new_val;
192 } elsif( !exists( $self->{
'_param_hash'}{$param_name}) ) {
193 if (exists( $self->{
'_unsubstituted_param_hash'}{$param_name} ) ) {
194 my $ini_used_missing_param = $self->{
'_used_missing_params'};
195 delete $self->{
'_used_missing_params'};
196 my $unsubstituted = $self->{
'_unsubstituted_param_hash'}{$param_name};
197 my $substituted = $self->param_substitute( $unsubstituted );
198 if (my $failed_dep = $self->{
'_used_missing_params'}) {
199 delete $self->{
'_used_missing_params'};
200 delete $self->{
'_substitution_in_progress'};
201 die
"ParamError: the evaluation of '$param_name' requires '$failed_dep' which is missing\n";
203 $self->{
'_param_hash'}{$param_name} = $substituted;
204 $self->{
'_used_missing_params'} = $ini_used_missing_param
if $ini_used_missing_param;
206 $self->{
'_used_missing_params'} = $param_name;
209 # The parameter has already been substituted
212 return exists( $self->{
'_param_hash'}{$param_name} )
213 ? $self->{
'_param_hash'}{$param_name}
218 =head2 param_required
220 Arg [1] :
string $param_name
222 Description: A strict getter method
for a job
's parameter; will die if the parameter was not set or is undefined
224 Example : my $source = $self->param_required('source
');
226 Returntype : any Perl structure or object that you dared to store
232 my $param_name = shift @_;
234 my $value = $self->_param_silent($param_name);
236 return defined( $value )
238 : die "ParamError: value for param_required('$param_name
') is required and has to be defined\n";
244 Arg [1] : string $param_name
246 Description: A predicate tester for whether the parameter has been initialized (even to undef)
248 Example : if( $self->param_exists('source
') ) { print "'source
' exists\n"; } else { print "never heard of 'source
'\n"; }
256 my $param_name = shift @_;
258 $self->_param_silent($param_name);
259 if (exists( $self->{'_param_hash
'}{$param_name} )) {
261 } elsif (exists( $self->{'_unsubstituted_param_hash
'}{$param_name} )) {
262 # In this case, the substitution failed
269 =head2 param_is_defined
271 Arg [1] : string $param_name
273 Description: A predicate tester for definedness of a parameter
275 Example : if( $self->param_is_defined('source
') ) { print "defined, possibly zero"; } else { print "undefined"; }
281 sub param_is_defined {
283 my $param_name = shift @_;
285 my $value = $self->_param_silent($param_name);
286 if (exists( $self->{'_param_hash
'}{$param_name} )) {
287 return (defined $value ? 1 : 0);
288 } elsif (exists( $self->{'_unsubstituted_param_hash
'}{$param_name} )) {
289 # In this case, the substitution failed
299 Arg [1] : string $param_name
301 Arg [2] : (optional) $param_value
303 Arg [3] : (optional) $value_needs_substitution (in case you want to define a parameter with '#other_param#
' and let the system compute its true value later)
305 Description: A getter/setter method for a job's parameters that are initialized through multiple levels of precedence (see param_init() )
307 Example 1 : my $source = $self->param('source');
# acting as a getter
309 Example 2 : $self->param(
'binpath',
'/software/ensembl/compara'); # acting as a setter
311 Returntype : any Perl structure or
object that you dared to store
317 my $param_name = shift @_
318 or
throw(
"ParamError: calling param() without arguments");
320 my $value = $self->_param_silent( $param_name, @_ );
322 unless( exists( $self->{
'_param_hash'}{$param_name} )) {
323 warn
"ParamWarning: value for param('$param_name') is used before having been initialized!\n";
330 =head2 param_substitute
332 Arg [1] : Perl structure $string_with_templates
334 Description: Performs parameter substitution on strings that contain templates like
" #param_name# followed by #another_param_name# " .
336 Returntype : *another* Perl structure with matching topology (may be more complex as a result of substituting a substructure
for a term)
340 sub param_substitute {
341 my ($self, $structure, $overriding_hash) = @_;
343 my $ref_type = ref($structure);
351 } elsif($structure=~/^(?:#(expr\(.+?\)expr|[\w:]+)#)$/) { #
if the given
string is one complete substitution, we don
't want to force the output into a string
353 return $self->_subst_one_hashpair($1, $overriding_hash);
356 my $scalar_defined = 1;
358 $structure=~s/(?:#(expr\(.+?\)expr|[\w:]+)#)/my $value = $self->_subst_one_hashpair($1, $overriding_hash); $scalar_defined &&= defined($value); $value/eg;
360 return $scalar_defined ? $structure : undef;
363 } elsif($ref_type eq 'ARRAY
') {
364 my @substituted_array = ();
365 foreach my $element (@$structure) {
366 push @substituted_array, $self->param_substitute($element, $overriding_hash);
368 return \@substituted_array;
369 } elsif($ref_type eq 'HASH
') {
370 my %substituted_hash = ();
371 while(my($key,$value) = each %$structure) {
372 $substituted_hash{$self->param_substitute($key, $overriding_hash)} = $self->param_substitute($value, $overriding_hash);
374 return \%substituted_hash;
376 warn "Could not substitute parameters in '$structure
' - unsupported data type '$ref_type
'\n";
382 sub mysql_conn { # an example stringification formatter (others can be defined here or in a descendent of Params)
383 my ($self, $db_conn) = @_;
385 if(ref($db_conn) eq 'HASH
') {
386 return "--host=$db_conn->{-host} --port=$db_conn->{-port} --user='$db_conn->{-user}
' --password='$db_conn->{-pass}
' $db_conn->{-dbname}";
388 my $dbc = go_figure_dbc( $db_conn );
389 return '--host=
'.$dbc->host.' --port=
'.$dbc->port." --user='".$dbc->username."' --password='".$dbc->password."' ".$dbc->dbname;
393 sub mysql_dbname { # another example stringification formatter
394 my ($self, $db_conn) = @_;
396 if(ref($db_conn) eq 'HASH
') {
397 return $db_conn->{-dbname};
399 my $dbc = go_figure_dbc( $db_conn );
404 sub csvq { # another example stringification formatter
405 my ($self, $list) = @_;
407 return join(',
', map { "'$_
'" } @$list);
410 #--------------------------------------------[private methods]----------------------------------------------
412 =head2 _subst_one_hashpair
414 Description: this is a private method that performs one substitution. Called by param_substitute().
418 sub _subst_one_hashpair {
419 my ($self, $inside_hashes, $overriding_hash) = @_;
421 if($self->{'_substitution_in_progress
'}{$inside_hashes}++) {
422 die "ParamError: substitution loop among {".join(',
', map {"'$_
'"} keys %{$self->{'_substitution_in_progress
'}})."} has been detected\n";
427 # FIXME does not allow substitution of parameters names that have non-alphanumeric characters
428 if($inside_hashes=~/^\w+$/) {
430 $value = $self->_param_possibly_overridden($inside_hashes, $overriding_hash);
432 } elsif($inside_hashes=~/^(\w+):(\w+)$/) {
434 $value = $self->$1($self->_param_possibly_overridden($2, $overriding_hash));
436 } elsif($inside_hashes=~/^expr\((.*)\)expr$/) {
440 if($expression=~/\$\w+/) {
441 warn "ParamWarning: possibly using old substitution syntax in expression '$expression
'; please use new syntax '#alpha#
' instead of old '\$alpha
'.\n";
444 $expression=~s{(?:#(\w+)#)}{\$self->_param_possibly_overridden('$1
', \$overriding_hash)}g;
446 $value = eval "return ($expression)"; # NB: 'return' is needed to protect the hashrefs from being interpreted as scoping blocks
447 # and parentheses are needed because return binds stronger than 'and
' and 'or
'
450 delete $self->{'_substitution_in_progress
'}{$inside_hashes}; # to allow re-entering the sub
451 die $@ if $@ =~ /^ParamError/; # re-raise the underlying Param error
452 die "ParamError: Cannot evaluate the expression: '$inside_hashes
' ==> '$expression
'\n$@";
456 delete $self->{'_substitution_in_progress
'}{$inside_hashes};