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