Skip to content

Commit 802171a

Browse files
committed
Add autosync to run, OneOff sync mode
1 parent 287170a commit 802171a

File tree

9 files changed

+76
-13
lines changed

9 files changed

+76
-13
lines changed

docs/guide/config.md

+5
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ use-uv = false
8989
# to `true` when uv is enabled and `false` otherwise.
9090
autosync = true
9191

92+
# Enable or disable automatic `sync` ahead of `run` and `test`. This defaults
93+
# to `true` when uv is enabled and `false` otherwise. Note that autosync invoked
94+
# before `run` and `test` will never update your lockfiles.
95+
autosync-before-run = true
96+
9297
# Marks the managed .venv in a way that cloud based synchronization systems
9398
# like Dropbox and iCloud Files will not upload it. This defaults to true
9499
# as a .venv in cloud storage typically does not make sense. Set this to

rye/src/cli/add.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
303303
}
304304

305305
if (cfg.autosync() && !cmd.no_sync) || cmd.sync {
306-
autosync(&pyproject_toml, output)?;
306+
autosync(&pyproject_toml, output, false)?;
307307
}
308308

309309
Ok(())

rye/src/cli/remove.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
6565
}
6666

6767
if (Config::current().autosync() && !cmd.no_sync) || cmd.sync {
68-
autosync(&pyproject_toml, output)?;
68+
autosync(&pyproject_toml, output, false)?;
6969
}
7070

7171
Ok(())

rye/src/cli/run.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use anyhow::{bail, Context, Error};
88
use clap::Parser;
99
use console::style;
1010

11+
use crate::config::Config;
1112
use crate::pyproject::{PyProject, Script};
12-
use crate::sync::{sync, SyncOptions};
13+
use crate::sync::{autosync, sync, SyncOptions};
1314
use crate::tui::redirect_to_stderr;
14-
use crate::utils::{exec_spawn, get_venv_python_bin, success_status, IoPathContext};
15+
use crate::utils::{exec_spawn, get_venv_python_bin, success_status, CommandOutput, IoPathContext};
1516

1617
/// Runs a command installed into this package.
1718
#[derive(Parser, Debug)]
@@ -26,6 +27,18 @@ pub struct Args {
2627
/// Use this pyproject.toml file
2728
#[arg(long, value_name = "PYPROJECT_TOML")]
2829
pyproject: Option<PathBuf>,
30+
/// Runs `sync` even if auto-sync is disabled.
31+
#[arg(long)]
32+
sync: bool,
33+
/// Does not run `sync` even if auto-sync is enabled.
34+
#[arg(long, conflicts_with = "sync")]
35+
no_sync: bool,
36+
/// Enables verbose diagnostics.
37+
#[arg(short, long)]
38+
verbose: bool,
39+
/// Turns off all output.
40+
#[arg(short, long, conflicts_with = "verbose")]
41+
quiet: bool,
2942
}
3043

3144
#[derive(Parser, Debug)]
@@ -36,11 +49,16 @@ enum Cmd {
3649

3750
pub fn execute(cmd: Args) -> Result<(), Error> {
3851
let _guard = redirect_to_stderr(true);
52+
let output = CommandOutput::from_quiet_and_verbose(cmd.quiet, cmd.verbose);
3953
let pyproject = PyProject::load_or_discover(cmd.pyproject.as_deref())?;
4054

41-
// make sure we have the minimal virtualenv.
42-
sync(SyncOptions::python_only().pyproject(cmd.pyproject))
43-
.context("failed to sync ahead of run")?;
55+
if (Config::current().autosync_before_run() && !cmd.no_sync) || cmd.sync {
56+
autosync(&pyproject, output, true)?;
57+
} else {
58+
// make sure we have the minimal virtualenv.
59+
sync(SyncOptions::python_only().pyproject(cmd.pyproject))
60+
.context("failed to sync ahead of run")?;
61+
}
4462

4563
if cmd.list || cmd.cmd.is_none() {
4664
return list_scripts(&pyproject);

rye/src/cli/test.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ pub struct Args {
3131
// Disable test output capture to stdout
3232
#[arg(long = "no-capture", short = 's')]
3333
no_capture: bool,
34+
/// Runs `sync` even if auto-sync is disabled.
35+
#[arg(long)]
36+
sync: bool,
37+
/// Does not run `sync` even if auto-sync is enabled.
38+
#[arg(long, conflicts_with = "sync")]
39+
no_sync: bool,
3440
/// Enables verbose diagnostics.
3541
#[arg(short, long)]
3642
verbose: bool,
@@ -72,8 +78,8 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
7278
if !pytest.is_file() {
7379
let has_pytest = has_pytest_dependency(&projects)?;
7480
if has_pytest {
75-
if Config::current().autosync() {
76-
autosync(&projects[0], output)?;
81+
if (Config::current().autosync_before_run() && !cmd.no_sync) || cmd.sync {
82+
autosync(&projects[0], output, true)?;
7783
} else {
7884
bail!("pytest not installed but in dependencies. Run `rye sync`.")
7985
}

rye/src/config.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,23 @@ impl Config {
251251
Ok(rv)
252252
}
253253

254-
/// Enable autosync.
254+
/// Enable autosync for `add` and `remove`.
255255
pub fn autosync(&self) -> bool {
256256
self.doc
257257
.get("behavior")
258258
.and_then(|x| x.get("autosync"))
259259
.and_then(|x| x.as_bool())
260260
.unwrap_or_else(|| self.use_uv())
261261
}
262+
263+
/// Enable autosync for `run` and `test`.
264+
pub fn autosync_before_run(&self) -> bool {
265+
self.doc
266+
.get("behavior")
267+
.and_then(|x| x.get("autosync-before-run"))
268+
.and_then(|x| x.as_bool())
269+
.unwrap_or_else(|| self.use_uv())
270+
}
262271

263272
/// Indicates if uv should be used instead of pip-tools.
264273
pub fn use_uv(&self) -> bool {

rye/src/sync.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum SyncMode {
3737
Regular,
3838
/// recreate everything
3939
Full,
40+
/// Recreate if no lock file present, otherwise install without updating
41+
OneOff,
4042
}
4143

4244
/// Updates the virtualenv based on the pyproject.toml
@@ -177,16 +179,19 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> {
177179
// hack to make this work for now. We basically sym-link pip itself
178180
// into a folder all by itself and place a second file in there which we
179181
// can pass to pip-sync to install the local package.
182+
let has_lock = (if cmd.dev { &dev_lockfile } else { &lockfile }).is_file();
180183
if recreate || cmd.mode != SyncMode::PythonOnly {
181184
let sources = ExpandedSources::from_sources(&pyproject.sources()?)?;
182185
if cmd.no_lock {
183186
let lockfile = if cmd.dev { &dev_lockfile } else { &lockfile };
184-
if !lockfile.is_file() {
187+
if !has_lock {
185188
bail!(
186189
"Locking is disabled but lockfile '{}' does not exist",
187190
lockfile.display()
188191
);
189192
}
193+
} else if cmd.mode == SyncMode::OneOff && has_lock {
194+
// do nothing
190195
} else if let Some(workspace) = pyproject.workspace() {
191196
// make sure we have an up-to-date lockfile
192197
update_workspace_lockfile(
@@ -310,11 +315,11 @@ pub fn sync(mut cmd: SyncOptions) -> Result<(), Error> {
310315
}
311316

312317
/// Performs an autosync.
313-
pub fn autosync(pyproject: &PyProject, output: CommandOutput) -> Result<(), Error> {
318+
pub fn autosync(pyproject: &PyProject, output: CommandOutput, one_off: bool) -> Result<(), Error> {
314319
sync(SyncOptions {
315320
output,
316321
dev: true,
317-
mode: SyncMode::Regular,
322+
mode: if one_off {SyncMode::OneOff} else {SyncMode::Regular},
318323
force: false,
319324
no_lock: false,
320325
lock_options: LockOptions::default(),

rye/tests/test_cli.rs

+4
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,9 @@ fn test_dotenv() {
4747
42 23
4848
4949
----- stderr -----
50+
Reusing already existing virtualenv
51+
Installing dependencies
52+
Audited 1 package in [EXECUTION_TIME]
53+
Done!
5054
"###);
5155
}

rye/tests/test_init.rs

+16
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ fn test_init_lib() {
4141
Hello from my-project!
4242
4343
----- stderr -----
44+
Reusing already existing virtualenv
45+
Installing dependencies
46+
Audited 1 package in [EXECUTION_TIME]
47+
Done!
4448
"###);
4549

4650
assert!(
@@ -89,6 +93,10 @@ fn test_init_default() {
8993
Hello from my-project!
9094
9195
----- stderr -----
96+
Reusing already existing virtualenv
97+
Installing dependencies
98+
Audited 1 package in [EXECUTION_TIME]
99+
Done!
92100
"###);
93101

94102
assert!(
@@ -138,6 +146,10 @@ fn test_init_script() {
138146
Hello from my-project!
139147
140148
----- stderr -----
149+
Reusing already existing virtualenv
150+
Installing dependencies
151+
Audited 1 package in [EXECUTION_TIME]
152+
Done!
141153
"###);
142154

143155
rye_cmd_snapshot!(space.rye_cmd().arg("run").arg("python").arg("-mmy_project"), @r###"
@@ -147,6 +159,10 @@ fn test_init_script() {
147159
Hello from my-project!
148160
149161
----- stderr -----
162+
Reusing already existing virtualenv
163+
Installing dependencies
164+
Audited 1 package in [EXECUTION_TIME]
165+
Done!
150166
"###);
151167
}
152168

0 commit comments

Comments
 (0)