command.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import sys
  2. import sysconfig
  3. from os.path import dirname, abspath, join
  4. import shutil
  5. from typing import List, Optional, Tuple, Union
  6. import shlex
  7. def is_command_available(command_name):
  8. """
  9. Use `shutil.which` to determine whether a command is available.
  10. Args:
  11. command_name (str): The name of the command to check.
  12. Returns:
  13. bool: True if the command is available, False otherwise.
  14. """
  15. return shutil.which(command_name) is not None
  16. def find_parameter(parameters: List[str], param_names: List[str]) -> Optional[str]:
  17. """
  18. Find specified parameter by name from the parameters.
  19. Return the value of the parameter if found, otherwise return None.
  20. """
  21. if parameters is None:
  22. return None
  23. for i, param in enumerate(parameters):
  24. # Strip whitespace from the parameter
  25. param_stripped = param.strip()
  26. if '=' in param_stripped:
  27. key, value = param_stripped.split('=', 1)
  28. if key.strip().lstrip('-') in param_names:
  29. return value
  30. elif ' ' in param_stripped:
  31. key, value = param_stripped.split(' ', 1)
  32. if key.strip().lstrip('-') in param_names:
  33. split_values = shlex.split(value)
  34. if len(split_values) == 1:
  35. return split_values[0]
  36. return value
  37. else:
  38. if param_stripped.lstrip('-') in param_names:
  39. if i + 1 < len(parameters):
  40. return parameters[i + 1]
  41. return None
  42. def find_int_parameter(parameters: List[str], param_names: List[str]) -> Optional[int]:
  43. """
  44. Find specified integer parameter by name from the parameters.
  45. Return the integer value of the parameter if found, otherwise return None.
  46. """
  47. value = find_parameter(parameters, param_names)
  48. if value is not None:
  49. try:
  50. return int(value)
  51. except ValueError:
  52. return None
  53. return None
  54. def find_bool_parameter(parameters: List[str], param_names: List[str]) -> bool:
  55. """
  56. Find specified boolean parameter by name from the parameters.
  57. Return True if the parameter is set, otherwise return False.
  58. """
  59. if parameters is None:
  60. return False
  61. for i, param in enumerate(parameters):
  62. param_stripped = param.strip()
  63. if param_stripped.lstrip('-') in param_names:
  64. return True
  65. return False
  66. def get_versioned_command(command_name: str, version: str) -> str:
  67. """
  68. Get the versioned command name.
  69. """
  70. if command_name.endswith(".exe"):
  71. return f"{command_name[:-4]}_{version}.exe"
  72. return f"{command_name}_{version}"
  73. def get_command_path(command_name: str) -> str:
  74. """
  75. Return the full path of sepcified command. Supports both frozen and python base environments.
  76. """
  77. base_path = (
  78. dirname(sys.executable)
  79. if getattr(sys, 'frozen', False)
  80. else sysconfig.get_path("scripts")
  81. )
  82. return abspath(join(base_path, command_name))
  83. def extend_args_no_exist(
  84. arguments: List[str], *args: Union[str, Tuple[str, str]]
  85. ) -> None:
  86. """
  87. Extend arguments list with key-value pairs only if the key is not already present.
  88. This function prevents duplicate parameters when user-defined backend parameters
  89. may conflict with system-generated ones.
  90. Args:
  91. arguments: The list of arguments to extend (modified in place)
  92. *args: Variable number of arguments, each can be:
  93. - A tuple of (key, value) like ("--host", "127.0.0.1")
  94. - A single string key like "--enable-metrics" (flag without value)
  95. Examples:
  96. extend_args_no_exist(args, ("--host", "127.0.0.1"), ("--port", "8080"))
  97. extend_args_no_exist(args, "--enable-metrics")
  98. """
  99. for arg in args:
  100. if isinstance(arg, tuple):
  101. key, value = arg
  102. if not any(
  103. existing_arg == key or existing_arg.startswith(f"{key}=")
  104. for existing_arg in arguments
  105. ):
  106. arguments.extend([key, value])
  107. else:
  108. # Single flag without value
  109. if arg not in arguments:
  110. arguments.append(arg)
  111. def format_backend_parameters(parameters: Optional[List[str]]) -> List[str]:
  112. """
  113. Format flattened command-line tokens as backend parameter entries.
  114. Examples:
  115. ["--max-model-len", "8192", "--enable-prefix-caching"]
  116. -> ["--max-model-len=8192", "--enable-prefix-caching"]
  117. ["--dtype=float16"] -> ["--dtype=float16"]
  118. """
  119. if not parameters:
  120. return []
  121. formatted = []
  122. index = 0
  123. while index < len(parameters):
  124. parameter = parameters[index]
  125. if not parameter.startswith("-") or "=" in parameter:
  126. formatted.append(parameter)
  127. index += 1
  128. continue
  129. if index + 1 < len(parameters) and not _looks_like_parameter(
  130. parameters[index + 1]
  131. ):
  132. formatted.append(f"{parameter}={parameters[index + 1]}")
  133. index += 2
  134. continue
  135. formatted.append(parameter)
  136. index += 1
  137. return formatted
  138. def _looks_like_parameter(value: str) -> bool:
  139. if not value.startswith("-"):
  140. return False
  141. # Negative numeric values are parameter values, not flags.
  142. try:
  143. float(value)
  144. return False
  145. except ValueError:
  146. return True