Coverage Report

Created: 2026-03-12 02:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/udf/python/python_env.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <gen_cpp/BackendService_types.h>
21
22
#include <filesystem>
23
#include <utility>
24
25
#include "common/status.h"
26
27
namespace doris {
28
29
namespace fs = std::filesystem;
30
31
enum class PythonEnvType { CONDA, VENV };
32
33
struct PythonVersion {
34
    std::string full_version;    // e.g. "3.9.16"
35
    std::string base_path;       // e.g. "/root/anaconda3/envs/python3.9"
36
    std::string executable_path; // e.g. "{base_path}/bin/python3"
37
38
8
    PythonVersion() = default;
39
40
    explicit PythonVersion(std::string full_version, std::string base_path,
41
                           std::string executable_path)
42
54
            : full_version(std::move(full_version)),
43
54
              base_path(std::move(base_path)),
44
54
              executable_path(std::move(executable_path)) {}
45
46
17
    bool operator==(const PythonVersion& other) const {
47
17
        return full_version == other.full_version && base_path == other.base_path &&
48
17
               executable_path == other.executable_path;
49
17
    }
50
51
1
    const std::string& get_base_path() const { return base_path; }
52
53
58
    const std::string& get_executable_path() const { return executable_path; }
54
55
28
    bool is_valid() const {
56
28
        return !full_version.empty() && !base_path.empty() && !executable_path.empty() &&
57
28
               fs::exists(base_path) && fs::exists(executable_path);
58
28
    }
59
60
26
    std::string to_string() const {
61
26
        return fmt::format("[full_version: {}, base_path: {}, executable_path: {}]", full_version,
62
26
                           base_path, executable_path);
63
26
    }
64
};
65
66
struct PythonEnvironment {
67
    std::string env_name; // e.g. "base" or "myenv"
68
    PythonVersion python_version;
69
70
    PythonEnvironment(const std::string& name, const PythonVersion& python_version);
71
72
    std::string to_string() const;
73
74
    bool is_valid() const;
75
76
    static Status scan_from_conda_root_path(const fs::path& conda_root_path,
77
                                            std::vector<PythonEnvironment>* environments);
78
79
    static Status scan_from_venv_root_path(const fs::path& venv_root_path,
80
                                           const std::vector<std::string>& interpreter_paths,
81
                                           std::vector<PythonEnvironment>* environments);
82
};
83
84
class PythonEnvScanner {
85
public:
86
13
    PythonEnvScanner(const fs::path& env_root_path) : _env_root_path(env_root_path) {}
87
88
13
    virtual ~PythonEnvScanner() = default;
89
90
    virtual Status scan() = 0;
91
92
    Status get_versions(std::vector<PythonVersion>* versions) const;
93
94
    Status get_version(const std::string& runtime_version, PythonVersion* version) const;
95
96
0
    const std::vector<PythonEnvironment>& get_envs() const { return _envs; }
97
98
2
    std::string root_path() const { return _env_root_path.string(); }
99
100
    virtual PythonEnvType env_type() const = 0;
101
102
    virtual std::string to_string() const = 0;
103
104
protected:
105
    fs::path _env_root_path;
106
    std::vector<PythonEnvironment> _envs;
107
};
108
109
class CondaEnvScanner : public PythonEnvScanner {
110
public:
111
9
    CondaEnvScanner(const fs::path& python_root_path) : PythonEnvScanner(python_root_path) {}
112
113
9
    ~CondaEnvScanner() override = default;
114
115
    Status scan() override;
116
117
    std::string to_string() const override;
118
119
1
    PythonEnvType env_type() const override { return PythonEnvType::CONDA; }
120
};
121
122
class VenvEnvScanner : public PythonEnvScanner {
123
public:
124
    VenvEnvScanner(const fs::path& python_root_path,
125
                   const std::vector<std::string>& interpreter_paths)
126
4
            : PythonEnvScanner(python_root_path), _interpreter_paths(interpreter_paths) {}
127
128
4
    ~VenvEnvScanner() override = default;
129
130
    Status scan() override;
131
132
    std::string to_string() const override;
133
134
1
    PythonEnvType env_type() const override { return PythonEnvType::VENV; }
135
136
private:
137
    std::vector<std::string> _interpreter_paths;
138
};
139
140
class PythonVersionManager {
141
public:
142
0
    static PythonVersionManager& instance() {
143
0
        static PythonVersionManager instance;
144
0
        return instance;
145
0
    }
146
147
    Status init(PythonEnvType env_type, const fs::path& python_root_path,
148
                const std::string& python_venv_interpreter_paths);
149
150
0
    Status get_version(const std::string& runtime_version, PythonVersion* version) const {
151
0
        return _env_scanner->get_version(runtime_version, version);
152
0
    }
153
154
0
    const std::vector<PythonEnvironment>& get_envs() const { return _env_scanner->get_envs(); }
155
156
0
    PythonEnvType env_type() const { return _env_scanner->env_type(); }
157
158
0
    std::string to_string() const { return _env_scanner->to_string(); }
159
160
    std::vector<TPythonEnvInfo> env_infos_to_thrift() const;
161
162
    std::vector<TPythonPackageInfo> package_infos_to_thrift(
163
            const std::vector<std::pair<std::string, std::string>>& packages) const;
164
165
private:
166
    std::unique_ptr<PythonEnvScanner> _env_scanner;
167
};
168
169
// List installed pip packages for a given Python version.
170
// Returns pairs of (package_name, version).
171
Status list_installed_packages(const PythonVersion& version,
172
                               std::vector<std::pair<std::string, std::string>>* packages);
173
174
} // namespace doris
175
176
namespace std {
177
template <>
178
struct hash<doris::PythonVersion> {
179
50
    size_t operator()(const doris::PythonVersion& v) const noexcept {
180
50
        return hash<string> {}(v.full_version);
181
50
    }
182
};
183
} // namespace std