Files
xc-llm-ascend/vllm_ascend/profiling_config.py
SILONG ZENG 52086394ae [Lint]Style: Convert vllm-ascend/compilation to ruff format (#5912)
### What this PR does / why we need it?
Convert `vllm-ascend/compilation` to ruff format.

### Does this PR introduce _any_ user-facing change?
During this migration, we encountered some **errors** in our CI and
testing environments, such as:
```
vllm_ascend/utils.py:653: in <module>
    def register_ascend_customop(vllm_config: VllmConfig | None = None):
                                              ^^^^^^^^^^^^^^^^^
E   TypeError: unsupported operand type(s) for |: 'NoneType' and 'NoneType'
```

**1. Root Cause Analysis:**
The project uses a common pattern to break circular dependencies:
```python
if TYPE_CHECKING:
    from vllm.config import VllmConfig
else:
    VllmConfig = None  # Placeholder assigned at runtime
```
When Python parses the function definition `def
register_ascend_customop(vllm_config: VllmConfig | None)`, it attempts
to evaluate the expression `VllmConfig | None`.
Since `VllmConfig` is assigned `None` at runtime, the expression
effectively becomes `None | None`. In Python, `None` is an instance of
`NoneType`. While the `|` operator is implemented for Type objects
(classes), it is not supported for `NoneType` instances, leading to the
`TypeError` shown above.

**2. Solution:**
To maintain the modern `|` syntax required by our new linting standards
while preserving our dependency management strategy, I have introduced:
```python
from __future__ import annotations
```
at the top of the affected files. This enables **Postponed Evaluation of
Annotations (PEP 563)**.

**3. Impact and Benefits:**
- By enabling `annotations`, Python no longer executes the `VllmConfig |
None` operation during module load. Instead, it stores the annotation as
a string literal, completely avoiding the `None | None` calculation.
- We can keep the `VllmConfig = None` placeholders. This ensures that
other modules can still import these symbols without triggering an
`ImportError`, maintaining a stable dependency graph.
- IDEs and static type checkers (MyPy/Pyright) continue to resolve the
types correctly. This allows us to use modern syntax without sacrificing
type safety or runtime stability.
- The only side effect is that `__annotations__` will now return strings
instead of type objects. Since this module does not use runtime type
enforcement or reflection, this change has zero negative impact on
existing functionality.
### How was this patch tested?

- vLLM version: v0.13.0
- vLLM main:
11b6af5280

---------

Signed-off-by: MrZ20 <2609716663@qq.com>
2026-01-16 20:57:46 +08:00

194 lines
6.8 KiB
Python

#
# Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This file is a part of the vllm-ascend project.
#
"""
Service profiling configuration generator module.
This module generates the service_profiling_symbols.yaml configuration file
to ~/.config/vllm_ascend/ directory.
"""
import contextlib
import tempfile
from pathlib import Path
import vllm
from vllm.logger import logger
VLLM_VERSION = vllm.__version__
# Configuration file name
CONFIG_FILENAME = f"service_profiling_symbols.{VLLM_VERSION}.yaml"
# Hard-coded YAML content, default symbols changed by user can be added here.
SERVICE_PROFILING_SYMBOLS_YAML = """
# ===== Batch / Scheduler =====
- symbol: vllm.v1.engine.processor:Processor.process_inputs
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.batch_hookers:process_inputs
- symbol: vllm.v1.core.sched.scheduler:Scheduler.schedule
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.batch_hookers:schedule
name: batchFrameworkProcessing
- symbol: vllm.v1.core.sched.scheduler:Scheduler._free_request
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.batch_hookers:free_request
- symbol: vllm.v1.core.sched.scheduler:Scheduler.add_request
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.batch_hookers:add_request
# ===== KV Cache =====
- symbol: vllm.v1.core.kv_cache_manager:KVCacheManager.allocate_slots
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.kvcache_hookers:allocate_slots
- symbol: vllm.v1.core.kv_cache_manager:KVCacheManager.free
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.kvcache_hookers:free
- symbol: vllm.v1.core.kv_cache_manager:KVCacheManager.get_computed_blocks
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.kvcache_hookers:get_computed_blocks
# ===== Model Execute =====
- symbol: vllm.model_executor.layers.logits_processor:LogitsProcessor.forward
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.model_hookers:compute_logits
name: computing_logits
- symbol: vllm.v1.sample.sampler:Sampler.forward
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.model_hookers:sampler_forward
name: sample
- symbol: vllm.v1.executor.abstract:Executor.execute_model
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.model_hookers:execute_model
name: modelExec
- symbol: vllm.v1.executor.multiproc_executor:MultiprocExecutor.execute_model
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.model_hookers:execute_model
name: modelExec
- symbol: vllm_ascend.worker.model_runner_v1:NPUModelRunner.execute_model
name: modelRunnerExec
domain: ModelExecute
- symbol: vllm_ascend.worker.model_runner_v1:NPUModelRunner._update_states
name: _update_states
domain: ModelExecute
- symbol: vllm_ascend.worker.model_runner_v1:NPUModelRunner._prepare_inputs
name: _prepare_inputs
domain: ModelExecute
- symbol: vllm_ascend.utils:ProfileExecuteDuration.capture_async
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.model_hookers:capture_async
# ===== Request Lifecycle =====
- symbol: vllm.v1.engine.async_llm:AsyncLLM.add_request
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.request_hookers:add_request_async
- symbol: vllm.engine.async_llm_engine:AsyncLLMEngine.add_request
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.request_hookers:add_request_async
- symbol: vllm.v1.engine.output_processor:OutputProcessor.process_outputs
min_version: "0.9.1"
handler: msserviceprofiler.vllm_profiler.vllm_v1.request_hookers:process_outputs
"""
def get_config_dir() -> Path:
"""
Get the vllm_ascend configuration directory path.
Returns:
Path: The path to ~/.config/vllm_ascend/ directory.
"""
home_dir = Path.home()
config_dir = home_dir / ".config" / "vllm_ascend"
return config_dir
def _cleanup_temp_file(tmp_path: Path | None) -> None:
"""
Clean up a temporary file if it exists.
Args:
tmp_path: Path to the temporary file to clean up.
"""
if tmp_path is not None and tmp_path.exists():
with contextlib.suppress(OSError):
tmp_path.unlink()
def generate_service_profiling_config() -> Path | None:
"""
Generate the service_profiling_symbols.yaml configuration file
to ~/.config/vllm_ascend/ directory.
If the configuration file already exists, this function will skip
creating it and return the existing file path.
If any error occurs during file creation, it will be logged but
will not interrupt the execution. The function will return None
to indicate that the file could not be created.
Returns:
Optional[Path]: The path to the generated (or existing) configuration file.
Returns None if file creation failed.
"""
config_dir = get_config_dir()
config_file = config_dir / CONFIG_FILENAME
# Check if the configuration file already exists
if config_file.exists():
return config_file
# Create the configuration directory if it doesn't exist
try:
config_dir.mkdir(parents=True, exist_ok=True)
except (OSError, PermissionError) as e:
logger.error(f"Failed to create configuration directory {config_dir}: {e}", exc_info=True)
return None
# Write the configuration file atomically using a temporary file
# This ensures the file is only written if the write succeeds completely
tmp_path = None
try:
# Create a temporary file in the same directory for atomic write
with tempfile.NamedTemporaryFile(
mode="w", encoding="utf-8", dir=config_dir, delete=False, suffix=".tmp", prefix=CONFIG_FILENAME + "."
) as tmp_file:
tmp_file.write(SERVICE_PROFILING_SYMBOLS_YAML)
tmp_path = Path(tmp_file.name)
# Atomically replace the target file with the temporary file
tmp_path.replace(config_file)
return config_file
except (OSError, PermissionError) as e:
logger.error(f"Failed to write configuration file {config_file}: {e}", exc_info=True)
return None
finally:
# Clean up the temporary file if it wasn't successfully replaced
_cleanup_temp_file(tmp_path)