conda.xsh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # Copyright (C) 2012 Anaconda, Inc
  2. # SPDX-License-Identifier: BSD-3-Clause
  3. # Much of this forked from https://github.com/gforsyth/xonda
  4. # Copyright (c) 2016, Gil Forsyth, All rights reserved.
  5. # Original code licensed under BSD-3-Clause.
  6. try:
  7. # xonsh >= 0.18.0
  8. from xonsh.lib.lazyasd import lazyobject
  9. except:
  10. # xonsh < 0.18.0
  11. from xonsh.lazyasd import lazyobject
  12. if 'CONDA_EXE' not in ${...}:
  13. ![python -m conda init --dev out> conda-dev-init.sh]
  14. source-bash conda-dev-init.sh
  15. import os
  16. os.remove("conda-dev-init.sh")
  17. _REACTIVATE_COMMANDS = ('install', 'update', 'upgrade', 'remove', 'uninstall')
  18. @lazyobject
  19. def Env():
  20. from collections import namedtuple
  21. return namedtuple('Env', ['name', 'path', 'bin_dir', 'envs_dir'])
  22. def _parse_args(args=None):
  23. from argparse import ArgumentParser
  24. p = ArgumentParser(add_help=False)
  25. p.add_argument('command', nargs='?')
  26. p.add_argument('-h', '--help', dest='help', action='store_true', default=False)
  27. p.add_argument('-v', '--version', dest='version', action='store_true', default=False)
  28. ns, _ = p.parse_known_args(args)
  29. if ns.command == 'activate':
  30. p.add_argument('env_name_or_prefix', default='base')
  31. elif ns.command in _REACTIVATE_COMMANDS:
  32. p.add_argument('-n', '--name')
  33. p.add_argument('-p', '--prefix')
  34. parsed_args, _ = p.parse_known_args(args)
  35. return parsed_args
  36. def _raise_pipeline_error(pipeline):
  37. stdout = pipeline.out
  38. stderr = pipeline.err
  39. if pipeline.returncode != 0:
  40. message = ("exited with %s\nstdout: %s\nstderr: %s\n"
  41. "" % (pipeline.returncode, stdout, stderr))
  42. raise RuntimeError(message)
  43. return stdout.strip()
  44. def _conda_activate_handler(env_name_or_prefix):
  45. import os
  46. __xonsh__.execer.exec($($CONDA_EXE shell.xonsh activate @(env_name_or_prefix)),
  47. glbs=__xonsh__.ctx,
  48. filename="$(conda shell.xonsh activate " + env_name_or_prefix + ")")
  49. if $CONDA_DEFAULT_ENV != os.path.split(env_name_or_prefix)[1]:
  50. import sys as _sys
  51. print("WARNING: conda environment not activated properly. "
  52. "This is likely because you have a conda init inside of your "
  53. "~/.bashrc (unix) or *.bat activation file (windows). This is "
  54. "causing conda to activate twice in xonsh. Please remove the conda "
  55. "init block from your other shell.", file=_sys.stderr)
  56. def _conda_deactivate_handler():
  57. __xonsh__.execer.exec($($CONDA_EXE shell.xonsh deactivate),
  58. glbs=__xonsh__.ctx,
  59. filename="$(conda shell.xonsh deactivate)")
  60. def _conda_passthrough_handler(args):
  61. pipeline = ![$CONDA_EXE @(args)]
  62. _raise_pipeline_error(pipeline)
  63. def _conda_reactivate_handler(args, name_or_prefix_given):
  64. pipeline = ![$CONDA_EXE @(args)]
  65. _raise_pipeline_error(pipeline)
  66. if not name_or_prefix_given:
  67. __xonsh__.execer.exec($($CONDA_EXE shell.xonsh reactivate),
  68. glbs=__xonsh__.ctx,
  69. filename="$(conda shell.xonsh reactivate)")
  70. def _conda_main(args=None):
  71. parsed_args = _parse_args(args)
  72. if parsed_args.command == 'activate':
  73. _conda_activate_handler(parsed_args.env_name_or_prefix)
  74. elif parsed_args.command == 'deactivate':
  75. _conda_deactivate_handler()
  76. elif parsed_args.command in _REACTIVATE_COMMANDS:
  77. name_or_prefix_given = bool(parsed_args.name or parsed_args.prefix)
  78. _conda_reactivate_handler(args, name_or_prefix_given)
  79. else:
  80. _conda_passthrough_handler(args)
  81. if 'CONDA_SHLVL' not in ${...}:
  82. $CONDA_SHLVL = '0'
  83. import os as _os
  84. import sys as _sys
  85. _sys.path.insert(0, _os.path.join(_os.path.dirname(_os.path.dirname($CONDA_EXE)), "condabin"))
  86. del _os, _sys
  87. aliases['conda'] = _conda_main
  88. def _list_dirs(path):
  89. """Generator that lists the directories in a given path."""
  90. import os
  91. for entry in os.scandir(path):
  92. if not entry.name.startswith('.') and entry.is_dir():
  93. yield entry.name
  94. def _get_envs_unfiltered():
  95. """Grab a list of all conda env dirs from conda, allowing all warnings."""
  96. import os
  97. import importlib
  98. try:
  99. # breaking changes introduced in Anaconda 4.4.7
  100. # try to import newer library structure first
  101. context = importlib.import_module('conda.base.context')
  102. config = context.context
  103. except ModuleNotFoundError:
  104. config = importlib.import_module('conda.config')
  105. # create the list of environments
  106. env_list = []
  107. for envs_dir in config.envs_dirs:
  108. # skip non-existing environments directories
  109. if not os.path.exists(envs_dir):
  110. continue
  111. # for each environment in the environments directory
  112. for env_name in _list_dirs(envs_dir):
  113. # check for duplicates names
  114. if env_name in [env.name for env in env_list]:
  115. raise ValueError('Multiple environments with the same name '
  116. "in the system is not supported by conda's xonsh tools.")
  117. # add the environment to the list
  118. env_list.append(Env(name=env_name,
  119. path=os.path.join(envs_dir, env_name),
  120. bin_dir=os.path.join(envs_dir, env_name, 'bin'),
  121. envs_dir=envs_dir,
  122. ))
  123. return env_list
  124. def _get_envs():
  125. """Grab a list of all conda env dirs from conda, ignoring all warnings."""
  126. import warnings
  127. with warnings.catch_warnings():
  128. warnings.simplefilter("ignore")
  129. return _get_envs_unfiltered()
  130. def _conda_completer(prefix, line, start, end, ctx):
  131. """Completion for conda."""
  132. args = line.split(' ')
  133. possible = set()
  134. if len(args) == 0 or args[0] not in ['xonda', 'conda']:
  135. return None
  136. curix = args.index(prefix)
  137. if curix == 1:
  138. possible = {'activate', 'deactivate', 'install', 'remove', 'info',
  139. 'help', 'list', 'search', 'update', 'upgrade', 'uninstall',
  140. 'config', 'init', 'clean', 'package', 'bundle', 'env',
  141. 'select', 'create', '-h', '--help', '-V', '--version'}
  142. elif curix == 2:
  143. if args[1] in ['activate', 'select']:
  144. possible = set([env.name for env in _get_envs()])
  145. elif args[1] == 'create':
  146. possible = {'-p', '-n'}
  147. elif args[1] == 'env':
  148. possible = {'attach', 'create', 'export', 'list', 'remove',
  149. 'upload', 'update'}
  150. elif curix == 3:
  151. if args[2] == 'export':
  152. possible = {'-n', '--name'}
  153. elif args[2] == 'create':
  154. possible = {'-h', '--help', '-f', '--file', '-n', '--name', '-p',
  155. '--prefix', '-q', '--quiet', '--force', '--json',
  156. '--debug', '-v', '--verbose'}
  157. elif curix == 4:
  158. if args[2] == 'export' and args[3] in ['-n','--name']:
  159. possible = set([env.name for env in _get_envs()])
  160. return {i for i in possible if i.startswith(prefix)}
  161. # add _xonda_completer to list of completers
  162. __xonsh__.completers['conda'] = _conda_completer
  163. # bump to top of list
  164. __xonsh__.completers.move_to_end('conda', last=False)