ATLAS Offline Software
acmdlib.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2 
3 # @file PyUtils.acmdlib
4 # @purpose a library to ease the writing of sub-command scripts
5 # @author Sebastien Binet
6 # @date January 2010
7 
8 from __future__ import with_statement
9 
10 __doc__ = "a library to ease the writing of sub-command scripts"
11 __author__ = "Sebastien Binet"
12 
13 __all__ = [
14  'Command',
15  'command',
16  'argument',
17  'register',
18  ]
19 
20 
21 import argparse
22 import textwrap
23 import importlib
24 
25 
26 ACMD_GROUPNAME = 'acmdlib.commands'
27 """The name under which all commands are grouped
28 """
29 
30 ACMD_PARSER = argparse.ArgumentParser(
31  prog="acmd",
32  description="a general script interface with sub-commands",
33  )
34 
35 ACMD_SUBPARSERS = ACMD_PARSER.add_subparsers(
36  dest='command',
37  title='commands',
38  metavar='COMMAND',
39  )
40 
41 
42 class Command(object):
43  """A wrapper class to manage the creation of commands and their arguments
44 
45  this is very heavily inspired from:
46  http://pypi.python.org/pypi/django-boss (MIT licence)
47  """
48 
49  def __init__(self, fct, **kwargs):
50  object.__init__(self)
51  self.fct = fct
52  self.parser = self._make_parser(**kwargs)
53  self._init_arguments()
54  plugin_name = kwargs.get('name') or self.name
55  register(plugin_name, self.fct.__module__)
56 
57  @property
58  def name(self):
59  return self.fct.__name__.replace('_','-')
60 
61  @property
62  def help(self):
63  if getattr(self.fct, '__doc__', None):
64  # just the first line of the doc string
65  return self.fct.__doc__.splitlines()[0]
66 
67  @property
68  def description(self):
69  if getattr(self.fct, '__doc__', None):
70  return textwrap.dedent(self.fct.__doc__)
71 
72  @property
73  def add_argument(self):
74  return self.parser.add_argument
75 
76  def __call__(self, *args, **kwargs):
77  return self.fct(*args, **kwargs)
78 
79  def _make_parser(self, **kwargs):
80  """Create and register a subparser for this command."""
81 
82  kwargs.setdefault('help', self.help)
83  kwargs.setdefault('formatter_class',argparse.RawDescriptionHelpFormatter)
84  kwargs.setdefault('description', self.description)
85  kwargs.setdefault('name', self.name)
86  names = (kwargs.get('name') or self.name).split('.')
87 
88  def _get_subparser(a):
89  if a._subparsers:
90  for action in a._subparsers._actions:
91  if isinstance(action, argparse._SubParsersAction):
92  return action
93  raise RuntimeError('could not find adequate subparser')
94  return a.add_subparsers(dest='command',
95  title='commands',
96  metavar='COMMAND')
97  def _get_parser(node, idx, names):
98  name = names[idx]
99  if name in node.choices:
100  return node.choices[name]
101  args = {
102  'name' : name,
103  'help' : 'a group of sub-commands',
104  }
105  return node.add_parser(**args)
106 
107  parser = ACMD_PARSER
108  node = _get_subparser(parser)
109 
110  for i,n in enumerate(names[:-1]):
111  node = _get_subparser(parser)
112  parser = _get_parser(node, i, names)
113 
114  node = _get_subparser(parser)
115  kwargs['name'] = names[-1]
116  parser = node.add_parser(**kwargs)
117  return parser
118 
119  def add_mutually_exclusive_group(self, **kwargs):
120  return self.parser.add_mutually_exclusive_group(**kwargs)
121 
122  def _init_arguments(self):
123  if hasattr(self.fct, '_acmdlib_arguments'):
124  while self.fct._acmdlib_arguments:
125  args, kwargs = self.fct._acmdlib_arguments.pop()
126  self.add_argument(*args, **kwargs)
127 
128  if hasattr(self.fct,'_acmdlib_arguments_mutual'):
129  if not hasattr(self, '_acmdlib_mutual_group'):
131  while self.fct._acmdlib_arguments_mutual:
132  args, kwargs = self.fct._acmdlib_arguments_mutual.pop()
133  self._acmdlib_mutual_group.add_argument(*args, **kwargs)
134 
135  pass # Command
136 
138  """A simple plugin registration.
139  """
140  _plugins = {}
141 
142  @classmethod
143  def register(cls, name, value):
144  """Register plugin
145  """
146  cls._plugins[name] = value
147 
148  @classmethod
149  def exists(cls, name):
150  """Check if plugin exists
151  """
152  return name in cls._plugins
153 
154  @classmethod
155  def load(cls, name):
156  """Load given plugin and return it
157  """
158  try:
159  return importlib.import_module(cls._plugins[name])
160  except Exception as err:
161  print("** could not load command [%s]:\n%s" % (name, err))
162 
163  @classmethod
164  def loadAll(cls):
165  """Load all plugins
166  """
167  for k in cls._plugins.keys():
168  cls.load(k)
169 
170 
171 
172 def command(*args, **kwargs):
173  """Decorator to declare that a function is a command.
174  """
175  def deco(fct):
176  return Command(fct, **kwargs)
177  if args:
178  return deco(*args)
179  return deco
180 
181 def argument(*args, **kwargs):
182  """Decorator to add an argument to a command.
183  Use the `mutual=True` keyword to specify that the argument should belong to a mutual exclusion group.
184  """
185  is_mutual = kwargs.pop('mutual', False)
186 
187  def deco(fct):
188  if isinstance(fct, Command):
189  cmd = fct
190  if is_mutual:
191  if not hasattr(cmd, '_acmdlib_mutual_group'):
192  cmd._acmdlib_mutual_group = cmd.add_mutually_exclusive_group()
193  cmd._acmdlib_mutual_group.add_argument(*args, **kwargs)
194  else:
195  cmd.add_argument(*args, **kwargs)
196  else:
197  store_as = '_acmdlib_arguments_mutual' if is_mutual else '_acmdlib_arguments'
198  if not hasattr(fct, store_as):
199  setattr(fct, store_as, [])
200  getattr(fct, store_as).append((args, kwargs))
201  #print "===",args,kwargs,type(fct),fct
202  return fct
203  return deco
204 
205 def register(name, value):
206  """Registers a plugin, given a name and value.
207 
208  ex: register('check-file', 'PyUtils.CheckFileLib:fct')
209  """
210 
211  return Plugins.register(name, value)
python.acmdlib.Plugins.exists
def exists(cls, name)
Definition: acmdlib.py:149
python.acmdlib.Plugins.register
def register(cls, name, value)
Definition: acmdlib.py:143
python.acmdlib.Command._make_parser
def _make_parser(self, **kwargs)
Definition: acmdlib.py:79
python.acmdlib.Command.parser
parser
Definition: acmdlib.py:52
python.acmdlib.Command.name
def name(self)
Definition: acmdlib.py:58
python.acmdlib.argument
def argument(*args, **kwargs)
Definition: acmdlib.py:181
python.acmdlib.Command.help
def help(self)
Definition: acmdlib.py:62
python.acmdlib.Command.__call__
def __call__(self, *args, **kwargs)
Definition: acmdlib.py:76
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.acmdlib.Command.fct
fct
Definition: acmdlib.py:51
python.acmdlib.register
def register(name, value)
Definition: acmdlib.py:205
python.acmdlib.command
def command(*args, **kwargs)
Definition: acmdlib.py:172
python.acmdlib.Command.add_argument
def add_argument(self)
Definition: acmdlib.py:73
python.acmdlib.Command.__init__
def __init__(self, fct, **kwargs)
Definition: acmdlib.py:49
python.acmdlib.Plugins.loadAll
def loadAll(cls)
Definition: acmdlib.py:164
python.acmdlib.Command._acmdlib_mutual_group
_acmdlib_mutual_group
Definition: acmdlib.py:130
python.acmdlib.Command._init_arguments
def _init_arguments(self)
Definition: acmdlib.py:122
python.acmdlib.Plugins._plugins
_plugins
Definition: acmdlib.py:140
python.acmdlib.Plugins
Definition: acmdlib.py:137
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
python.acmdlib.Plugins.load
def load(cls, name)
Definition: acmdlib.py:155
python.acmdlib.Command
classes ----------------------------------------------------------------—
Definition: acmdlib.py:42
pickleTool.object
object
Definition: pickleTool.py:30
python.acmdlib.Command.add_mutually_exclusive_group
def add_mutually_exclusive_group(self, **kwargs)
Definition: acmdlib.py:119
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
python.acmdlib.Command.description
def description(self)
Definition: acmdlib.py:68
Trk::split
@ split
Definition: LayerMaterialProperties.h:38