complete_api.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. # expensive import, we import from the full API
  2. # to ensure getting all dataset methods from any extension
  3. import datalad.api as dlapi
  4. from datalad.interface.base import (
  5. Interface,
  6. get_interface_groups,
  7. load_interface,
  8. )
  9. from datalad.utils import get_wrapped_class
  10. from .api_utils import (
  11. get_cmd_displayname,
  12. get_cmd_params,
  13. )
  14. from .simplified_api import api as simple_api
  15. # mapping of command interface classes to interface group titles
  16. _cmd_group_lookup = {
  17. load_interface(cmd_spec): title
  18. for id_, title, cmds in sorted(get_interface_groups(), key=lambda x: x[0])
  19. for cmd_spec in cmds
  20. }
  21. # make each extension package its own group
  22. from datalad.support.entrypoints import iter_entrypoints
  23. for ename, _, (grp_descr, interfaces) in iter_entrypoints(
  24. 'datalad.extensions', load=True):
  25. for intfspec in interfaces:
  26. # turn the interface spec into an instance
  27. intf = load_interface(intfspec[:2])
  28. _cmd_group_lookup[intf] = grp_descr
  29. # all supported commands
  30. api = {}
  31. for mname in dir(dlapi):
  32. # iterate over all members of the Dataset class and find the
  33. # methods that are command interface callables
  34. # skip any private stuff
  35. if mname.startswith('_'):
  36. continue
  37. # right now, we are technically not able to handle GUI inception
  38. # and need to prevent launching multiple instances of this app.
  39. # we also do not want the internal gooey helpers
  40. if mname.startswith('gooey'):
  41. continue
  42. m = getattr(dlapi, mname)
  43. try:
  44. # if either of the following tests fails, this member is not
  45. # a datalad command
  46. cls = get_wrapped_class(m)
  47. assert issubclass(cls, Interface)
  48. except Exception:
  49. continue
  50. cmd_spec = dict(name=get_cmd_displayname({}, mname))
  51. cmd_group = _cmd_group_lookup.get(cls)
  52. if cmd_group:
  53. cmd_spec['group'] = cmd_group
  54. # order of parameters is defined by order in the signature of the command
  55. parameter_order = {p[0]: i for i, p in enumerate(get_cmd_params(m))}
  56. # but always put any existing `dataset` parameter first, because (minus a
  57. # few exceptions) it will define the scope of a command, and also influence
  58. # other parameter choices (list of available remotes, basedir, etc.).
  59. # therefore it is useful to have users process this first
  60. if 'dataset' in parameter_order:
  61. parameter_order['dataset'] = -1
  62. cmd_spec['parameter_order'] = parameter_order
  63. # inherit the hand-crafted constraints of the simple api, if possible
  64. simple_cmd_constraints = simple_api.get(
  65. mname, {}).get('parameter_constraints')
  66. if simple_cmd_constraints:
  67. cmd_spec['parameter_constraints'] = simple_cmd_constraints
  68. api[mname] = cmd_spec
  69. # commands that operate on datasets, are attached as methods to the
  70. # Dataset class
  71. dataset_api = {
  72. name: api[name]
  73. for name in dir(dlapi.Dataset)
  74. if name in api
  75. }
  76. gooey_suite = dict(
  77. title='&Complete (Development preview)',
  78. description='Generic access to all command available in this DataLad installation',
  79. apis=dict(
  80. dataset=dataset_api,
  81. directory=api,
  82. directory_in_ds=api,
  83. file=api,
  84. file_in_ds=api,
  85. annexed_file=api,
  86. other=api,
  87. ),
  88. # mapping of group name/title to sort index
  89. api_group_order={
  90. spec[1]: spec[0] for spec in get_interface_groups()
  91. },
  92. # these generic parameters never make sense
  93. exclude_parameters=set((
  94. # cmd execution wants a generator
  95. 'return_type',
  96. # could be useful internally, but a user cannot chain commands
  97. 'result_filter',
  98. # we cannot deal with non-dict results, and override any transform
  99. 'result_xfm',
  100. )),
  101. # generic name overrides
  102. parameter_display_names={},
  103. )