1# -*- coding: utf-8 -*-
2
3# ==============================================================================
4# COPYRIGHT (C) 1991 - 2015  EDF R&D                  WWW.CODE-ASTER.ORG
5# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8# (AT YOUR OPTION) ANY LATER VERSION.
9#
10# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14#
15# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17#    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
18# ==============================================================================
19
20"""
21This class define a 'light' modified parser :
22    - change exit method which exits using run.Sortie method
23    - add an action 'store_const_once' to the parser.
24"""
25
26import os
27from optparse import OptionParser, SUPPRESS_HELP, Option, OptionError, OptionValueError
28
29from asrun.common.i18n  import _
30from asrun.__pkginfo__  import version, copyright
31from asrun.mystring     import convert
32from asrun.core         import magic
33
34
35class AsRunOption(Option):
36    """Add 'store_const_once' action, it works like 'store_const' except that
37    a value can be stored only once, next occurences will raise an error.
38    """
39    ACTIONS = Option.ACTIONS + ("store_const_once",)
40    STORE_ACTIONS = Option.STORE_ACTIONS + ("store_const_once",)
41    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("store_const_once",)
42
43    def take_action (self, action, dest, opt, value, values, parser):
44        """Uses 'store_const_once' or standard actions.
45        """
46        if action == "store_const_once":
47            # ----- store_const_once
48            if not hasattr(values, dest) or not getattr(values, dest):
49                setattr(values, dest, getattr(self, 'const'))
50            else:
51                raise OptionValueError("%r is invalid because '%s' previously occurs" \
52                        % (getattr(self, 'const'), dest))
53        else:
54            # ----- standard actions
55            Option.take_action(self, action, dest, opt, value, values, parser)
56
57    def _check_const (self):
58        if self.action != "store_const" and self.action != "store_const_once" \
59                and getattr(self, 'const') is not None:
60            raise OptionError(
61                    "'const' must not be supplied for action %r" % self.action, self)
62
63    # ----- because of changes to private method _check_conf
64    CHECK_METHODS = [Option._check_action,
65                    Option._check_type,
66                    Option._check_choice,
67                    Option._check_dest,
68                    _check_const,
69                    Option._check_nargs,
70                    Option._check_callback]
71
72
73class AsRunParser(OptionParser):
74    """Modify lightly the standard parser.
75    """
76    def __init__(self, run, *args, **kwargs):
77        """Initialization."""
78        self.run = run
79        # set option_class = AsRunOption here
80        OptionParser.__init__(self, option_class=AsRunOption, *args, **kwargs)
81
82
83    def exit(self, status=0, msg=None):
84        """Call 'run.Sortie' method instead of 'sys.exit'."""
85        if msg:
86            magic.get_stderr().write(convert(msg))
87        self.run.PrintExitCode = False
88        self.run.Sortie(status)
89
90
91    #def get_usage(self):
92        #return to_unicode(OptionParser.get_usage(self))
93    def print_usage(self, file=magic.get_stdout()):
94        """Print the usage message for the current program"""
95        if self.usage:
96            print(self.get_usage(), file=file)
97
98
99# values used if arguments are not parsed (when using AsRunFactory for example)
100default_options = {
101    'verbose' : False,
102    'silent'  : False,
103    'num_job' : str(os.getpid()),
104}
105
106
107def define_parser(run):
108    """Build argument parser.
109    """
110    p = AsRunParser(run,
111        usage="""%prog action [options] [arguments]
112
113  Functions :
114""",
115        version="""as_run %s
116%s""" % (version, copyright))
117    p.add_option('-v', '--verbose',
118        action='store_true', dest='verbose', default=default_options['verbose'],
119        help=_('increase verbosity'))
120    p.add_option('--silent',
121        action='store_true', dest='silent', default=default_options['silent'],
122        help=_('run as silent as possible'))
123    p.add_option('-g', '--debug',
124        action='store_true', dest='debug', default=False,
125        help=_('print debugging information'))
126    p.add_option('--stdout',
127        action='store', dest='stdout', default=None, metavar='FILE',
128        help=_('allow to redirect messages usually written on sys.stdout'))
129    p.add_option('--stderr',
130        action='store', dest='stderr', default=None, metavar='FILE',
131        help=_('allow to redirect messages usually written on sys.stderr '
132                '(only asrun messages)'))
133    p.add_option('--log_progress',
134        action='store', dest='log_progress', default=None, metavar='FILE',
135        help=_('redirect progress informations to a file instead of sys.stderr'))
136    p.add_option('--nodebug_stderr',
137        action='store_false', dest='debug_stderr', default=True,
138        help=_('disable printing of debugging information to stderr'))
139    p.add_option('-f', '--force',
140        action='store_true', dest='force', default=False,
141        help=_('force operations which can be cached (download, ' \
142             'compilation...)'))
143    p.add_option('--num_job',
144        action='store', dest='num_job', default=default_options['num_job'],
145        help=SUPPRESS_HELP)
146    p.add_option('--display',
147        action='store', dest='display', default=None,
148        help=_('value of DISPLAY variable (NOTE : some functions read it from a file)'))
149    p.add_option('--rcdir',
150        action='store', dest='rcdir', default=None, metavar='DIR',
151        help=_("use resources directory $HOME/'DIR' (default is .astkrc). "
152            "Avoid absolute path because it will be passed to remote servers."))
153    # options which override the server configuration
154    #XXX howto to merge with SSHServer and co ?
155    p.add_option('--remote_shell_protocol',
156        action='store', dest='remote_shell_protocol', default='SSH',
157        help=_('remote protocol used for shell commands'))
158    p.add_option('--remote_copy_protocol',
159        action='store', dest='remote_copy_protocol', default='SCP',
160        help=_('remote protocol used to copy files and directories'))
161
162    return p
163
164
165def get_option_value(args_list, opt, default=None, action="store"):
166    """Parse the arguments 'args_list' and return value of the option named 'opt'."""
167    def fpass(err, *args, **kwargs):
168        """do not exit"""
169        raise
170    if not type(opt) in (list, tuple):
171        opt = [opt,]
172    kwargs = { "dest" : "var", "action" : action }
173    parser = OptionParser()
174    parser.error = fpass
175    parser.add_option(*opt, **kwargs)
176    value = None
177    if action == "store_true":
178        value = False
179    l_args = []
180    # --help would raise print_help of the working parser
181    if "-h" not in args_list and "--help" not in args_list:
182        for arg in args_list:
183            l_args.append(arg)
184            try:
185                options, args = parser.parse_args(l_args)
186                value = options.var
187            except Exception:
188                l_args.pop(-1)
189    if value is None:
190        value = default
191    return value
192