Skip to main content

uv_cli/
compat.rs

1use anyhow::{Result, anyhow};
2use clap::{Args, ValueEnum};
3
4use uv_warnings::warn_user;
5
6pub trait CompatArgs {
7    fn validate(&self) -> Result<()>;
8}
9
10/// Arguments for `pip-compile` compatibility.
11///
12/// These represent a subset of the `pip-compile` interface that uv supports by default.
13/// For example, users often pass `--allow-unsafe`, which is unnecessary with uv. But it's a
14/// nice user experience to warn, rather than fail, when users pass `--allow-unsafe`.
15#[derive(Args)]
16pub struct PipCompileCompatArgs {
17    #[clap(long, hide = true)]
18    allow_unsafe: bool,
19
20    #[clap(long, hide = true)]
21    no_allow_unsafe: bool,
22
23    #[clap(long, hide = true)]
24    reuse_hashes: bool,
25
26    #[clap(long, hide = true)]
27    no_reuse_hashes: bool,
28
29    #[clap(long, hide = true)]
30    resolver: Option<Resolver>,
31
32    #[clap(long, hide = true)]
33    max_rounds: Option<usize>,
34
35    #[clap(long, hide = true)]
36    client_cert: Option<String>,
37
38    #[clap(long, hide = true)]
39    emit_trusted_host: bool,
40
41    #[clap(long, hide = true)]
42    no_emit_trusted_host: bool,
43
44    #[clap(long, hide = true)]
45    config: Option<String>,
46
47    #[clap(long, hide = true)]
48    no_config: bool,
49
50    #[clap(long, hide = true)]
51    emit_options: bool,
52
53    #[clap(long, hide = true)]
54    no_emit_options: bool,
55
56    #[clap(long, hide = true)]
57    pip_args: Option<String>,
58}
59
60impl CompatArgs for PipCompileCompatArgs {
61    /// Validate the arguments passed for `pip-compile` compatibility.
62    ///
63    /// This method will warn when an argument is passed that has no effect but matches uv's
64    /// behavior. If an argument is passed that does _not_ match uv's behavior (e.g.,
65    /// `--no-build-isolation`), this method will return an error.
66    fn validate(&self) -> Result<()> {
67        if self.allow_unsafe {
68            warn_user!(
69                "pip-compile's `--allow-unsafe` has no effect (uv can safely pin `pip` and other packages)"
70            );
71        }
72
73        if self.no_allow_unsafe {
74            warn_user!(
75                "pip-compile's `--no-allow-unsafe` has no effect (uv can safely pin `pip` and other packages)"
76            );
77        }
78
79        if self.reuse_hashes {
80            return Err(anyhow!(
81                "pip-compile's `--reuse-hashes` is unsupported (uv doesn't reuse hashes)"
82            ));
83        }
84
85        if self.no_reuse_hashes {
86            warn_user!("pip-compile's `--no-reuse-hashes` has no effect (uv doesn't reuse hashes)");
87        }
88
89        if let Some(resolver) = self.resolver {
90            match resolver {
91                Resolver::Backtracking => {
92                    warn_user!(
93                        "pip-compile's `--resolver=backtracking` has no effect (uv always backtracks)"
94                    );
95                }
96                Resolver::Legacy => {
97                    return Err(anyhow!(
98                        "pip-compile's `--resolver=legacy` is unsupported (uv always backtracks)"
99                    ));
100                }
101            }
102        }
103
104        if self.max_rounds.is_some() {
105            return Err(anyhow!(
106                "pip-compile's `--max-rounds` is unsupported (uv always resolves until convergence)"
107            ));
108        }
109
110        if self.client_cert.is_some() {
111            return Err(anyhow!(
112                "pip-compile's `--client-cert` is unsupported (uv doesn't support dedicated client certificates)"
113            ));
114        }
115
116        if self.emit_trusted_host {
117            return Err(anyhow!(
118                "pip-compile's `--emit-trusted-host` is unsupported"
119            ));
120        }
121
122        if self.no_emit_trusted_host {
123            warn_user!(
124                "pip-compile's `--no-emit-trusted-host` has no effect (uv never emits trusted hosts)"
125            );
126        }
127
128        if self.config.is_some() {
129            return Err(anyhow!(
130                "pip-compile's `--config` is unsupported (uv does not use a configuration file)"
131            ));
132        }
133
134        if self.emit_options {
135            return Err(anyhow!(
136                "pip-compile's `--emit-options` is unsupported (try `--emit-build-options` instead)"
137            ));
138        }
139
140        if self.no_emit_options {
141            warn_user!("pip-compile's `--no-emit-options` has no effect (uv never emits options)");
142        }
143
144        if self.pip_args.is_some() {
145            return Err(anyhow!(
146                "pip-compile's `--pip-args` is unsupported (try passing arguments to uv directly)"
147            ));
148        }
149
150        Ok(())
151    }
152}
153
154/// Arguments for `pip list` compatibility.
155///
156/// These represent a subset of the `pip list` interface that uv supports by default.
157#[derive(Args)]
158pub struct PipListCompatArgs {
159    #[clap(long, hide = true)]
160    disable_pip_version_check: bool,
161}
162
163impl CompatArgs for PipListCompatArgs {
164    /// Validate the arguments passed for `pip list` compatibility.
165    ///
166    /// This method will warn when an argument is passed that has no effect but matches uv's
167    /// behavior. If an argument is passed that does _not_ match uv's behavior (e.g.,
168    /// `--disable-pip-version-check`), this method will return an error.
169    fn validate(&self) -> Result<()> {
170        if self.disable_pip_version_check {
171            warn_user!("pip's `--disable-pip-version-check` has no effect");
172        }
173
174        Ok(())
175    }
176}
177
178/// Arguments for `pip-sync` compatibility.
179///
180/// These represent a subset of the `pip-sync` interface that uv supports by default.
181#[derive(Args)]
182pub struct PipSyncCompatArgs {
183    #[clap(short, long, hide = true)]
184    ask: bool,
185
186    #[clap(long, hide = true)]
187    python_executable: Option<String>,
188
189    #[clap(long, hide = true)]
190    user: bool,
191
192    #[clap(long, hide = true)]
193    client_cert: Option<String>,
194
195    #[clap(long, hide = true)]
196    config: Option<String>,
197
198    #[clap(long, hide = true)]
199    no_config: bool,
200
201    #[clap(long, hide = true)]
202    pip_args: Option<String>,
203}
204
205impl CompatArgs for PipSyncCompatArgs {
206    /// Validate the arguments passed for `pip-sync` compatibility.
207    ///
208    /// This method will warn when an argument is passed that has no effect but matches uv's
209    /// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
210    /// return an error.
211    fn validate(&self) -> Result<()> {
212        if self.ask {
213            return Err(anyhow!(
214                "pip-sync's `--ask` is unsupported (uv never asks for confirmation)"
215            ));
216        }
217
218        if self.python_executable.is_some() {
219            return Err(anyhow!(
220                "pip-sync's `--python-executable` is unsupported (to install into a separate Python environment, try setting `VIRTUAL_ENV` instead)"
221            ));
222        }
223
224        if self.user {
225            return Err(anyhow!(
226                "pip-sync's `--user` is unsupported (use a virtual environment instead)"
227            ));
228        }
229
230        if self.client_cert.is_some() {
231            return Err(anyhow!(
232                "pip-sync's `--client-cert` is unsupported (uv doesn't support dedicated client certificates)"
233            ));
234        }
235
236        if self.config.is_some() {
237            return Err(anyhow!(
238                "pip-sync's `--config` is unsupported (uv does not use a configuration file)"
239            ));
240        }
241
242        if self.pip_args.is_some() {
243            return Err(anyhow!(
244                "pip-sync's `--pip-args` is unsupported (try passing arguments to uv directly)"
245            ));
246        }
247
248        Ok(())
249    }
250}
251
252#[derive(Debug, Copy, Clone, ValueEnum)]
253enum Resolver {
254    Backtracking,
255    Legacy,
256}
257
258/// Arguments for `venv` compatibility.
259///
260/// These represent a subset of the `virtualenv` interface that uv supports by default.
261#[derive(Args)]
262pub struct VenvCompatArgs {
263    #[clap(long, hide = true)]
264    no_seed: bool,
265
266    #[clap(long, hide = true)]
267    no_pip: bool,
268
269    #[clap(long, hide = true)]
270    no_setuptools: bool,
271
272    #[clap(long, hide = true)]
273    no_wheel: bool,
274}
275
276impl CompatArgs for VenvCompatArgs {
277    /// Validate the arguments passed for `venv` compatibility.
278    ///
279    /// This method will warn when an argument is passed that has no effect but matches uv's
280    /// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
281    /// return an error.
282    fn validate(&self) -> Result<()> {
283        if self.no_seed {
284            warn_user!(
285                "virtualenv's `--no-seed` has no effect (uv omits seed packages by default)"
286            );
287        }
288
289        if self.no_pip {
290            warn_user!("virtualenv's `--no-pip` has no effect (uv omits `pip` by default)");
291        }
292
293        if self.no_setuptools {
294            warn_user!(
295                "virtualenv's `--no-setuptools` has no effect (uv omits `setuptools` by default)"
296            );
297        }
298
299        if self.no_wheel {
300            warn_user!("virtualenv's `--no-wheel` has no effect (uv omits `wheel` by default)");
301        }
302
303        Ok(())
304    }
305}
306
307/// Arguments for `pip uninstall` compatibility.
308///
309/// These represent a subset of the `pip uninstall` interface that uv supports by default.
310#[derive(Args)]
311pub struct PipUninstallCompatArgs {
312    /// Don't ask for confirmation of uninstall deletions.
313    ///
314    /// This option is for compatibility with `pip uninstall` and has no effect.
315    #[clap(short, long, hide = true)]
316    yes: bool,
317
318    #[clap(long, hide = true)]
319    disable_pip_version_check: bool,
320}
321
322impl CompatArgs for PipUninstallCompatArgs {
323    /// Validate the arguments passed for `pip uninstall` compatibility.
324    ///
325    /// This method will warn when an argument is passed that has no effect but matches uv's
326    /// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
327    /// return an error.
328    fn validate(&self) -> Result<()> {
329        if self.yes {
330            warn_user!("`--yes` has no effect (uv never asks for confirmation)");
331        }
332
333        if self.disable_pip_version_check {
334            warn_user!("pip's `--disable-pip-version-check` has no effect");
335        }
336
337        Ok(())
338    }
339}
340
341/// Arguments for `pip install` compatibility.
342///
343/// These represent a subset of the `pip install` interface that uv supports by default.
344#[derive(Args)]
345pub struct PipInstallCompatArgs {
346    #[clap(long, hide = true)]
347    disable_pip_version_check: bool,
348
349    #[clap(long, hide = false)]
350    user: bool,
351}
352
353impl CompatArgs for PipInstallCompatArgs {
354    /// Validate the arguments passed for `pip install` compatibility.
355    ///
356    /// This method will warn when an argument is passed that has no effect but matches uv's
357    /// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
358    /// return an error.
359    fn validate(&self) -> Result<()> {
360        if self.disable_pip_version_check {
361            warn_user!("pip's `--disable-pip-version-check` has no effect");
362        }
363
364        if self.user {
365            return Err(anyhow!(
366                "pip's `--user` is unsupported (use a virtual environment instead)"
367            ));
368        }
369
370        Ok(())
371    }
372}
373
374/// Arguments for generic `pip` command compatibility.
375///
376/// These represent a subset of the `pip` interface that exists on all commands.
377#[derive(Args)]
378pub struct PipGlobalCompatArgs {
379    #[clap(long, hide = true)]
380    disable_pip_version_check: bool,
381}
382
383impl CompatArgs for PipGlobalCompatArgs {
384    /// Validate the arguments passed for `pip` compatibility.
385    ///
386    /// This method will warn when an argument is passed that has no effect but matches uv's
387    /// behavior. If an argument is passed that does _not_ match uv's behavior, this method will
388    /// return an error.
389    fn validate(&self) -> Result<()> {
390        if self.disable_pip_version_check {
391            warn_user!("pip's `--disable-pip-version-check` has no effect");
392        }
393
394        Ok(())
395    }
396}