diff --git a/docs/docs/usage/dependency.md b/docs/docs/usage/dependency.md index f0f850073f..7b26952af6 100644 --- a/docs/docs/usage/dependency.md +++ b/docs/docs/usage/dependency.md @@ -171,6 +171,10 @@ There are two similar commands to do this job with a slight difference: - `pdm sync` installs dependencies in the lock file and will error out if it doesn't exist. Besides, `pdm sync` can also remove unneeded packages if `--clean` option is given. +## Specify the lockfile to use + +You can specify another lockfile than the default `pdm.lock` by using the `-L/--lockfilie ` option or the `PDM_LOCKFILE` environment variable. + ### Select a subset of dependencies with CLI options Say we have a project with following dependencies: diff --git a/news/1038.feature.md b/news/1038.feature.md new file mode 100644 index 0000000000..07104d7033 --- /dev/null +++ b/news/1038.feature.md @@ -0,0 +1 @@ +Allow specifying lockfile other than `pdm.lock` by `--lockfile` option or `PDM_LOCKFILE` env var. diff --git a/pdm/cli/commands/add.py b/pdm/cli/commands/add.py index cc5369afd7..ce8a8c0c54 100644 --- a/pdm/cli/commands/add.py +++ b/pdm/cli/commands/add.py @@ -5,6 +5,7 @@ from pdm.cli.options import ( dry_run_option, install_group, + lockfile_option, packages_group, prerelease_option, save_strategy_group, @@ -19,6 +20,7 @@ class Command(BaseCommand): """Add package(s) to pyproject.toml and install them""" arguments = BaseCommand.arguments + [ + lockfile_option, save_strategy_group, update_strategy_group, prerelease_option, diff --git a/pdm/cli/commands/export.py b/pdm/cli/commands/export.py index 4fbe5b4ec9..847b8cb471 100644 --- a/pdm/cli/commands/export.py +++ b/pdm/cli/commands/export.py @@ -6,7 +6,7 @@ from pdm.cli.actions import resolve_candidates_from_lockfile from pdm.cli.commands.base import BaseCommand -from pdm.cli.options import groups_group +from pdm.cli.options import groups_group, lockfile_option from pdm.cli.utils import translate_groups from pdm.formats import FORMATS from pdm.models.candidates import Candidate @@ -18,6 +18,7 @@ class Command(BaseCommand): """Export the locked packages set to other formats""" def add_arguments(self, parser: argparse.ArgumentParser) -> None: + lockfile_option.add_to_parser(parser) parser.add_argument( "-f", "--format", diff --git a/pdm/cli/commands/install.py b/pdm/cli/commands/install.py index 53edfb158e..586c257b70 100644 --- a/pdm/cli/commands/install.py +++ b/pdm/cli/commands/install.py @@ -7,7 +7,7 @@ from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.run import run_script_if_present -from pdm.cli.options import dry_run_option, groups_group, install_group +from pdm.cli.options import dry_run_option, groups_group, install_group, lockfile_option from pdm.project import Project @@ -18,6 +18,7 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None: groups_group.add_to_parser(parser) install_group.add_to_parser(parser) dry_run_option.add_to_parser(parser) + lockfile_option.add_to_parser(parser) parser.add_argument( "--no-lock", dest="lock", diff --git a/pdm/cli/commands/lock.py b/pdm/cli/commands/lock.py index f228bd2b41..e9c4a8d9a8 100644 --- a/pdm/cli/commands/lock.py +++ b/pdm/cli/commands/lock.py @@ -4,14 +4,14 @@ from pdm.cli import actions from pdm.cli.commands.base import BaseCommand from pdm.cli.commands.run import run_script_if_present -from pdm.cli.options import no_isolation_option +from pdm.cli.options import lockfile_option, no_isolation_option from pdm.project import Project class Command(BaseCommand): """Resolve and lock dependencies""" - arguments = BaseCommand.arguments + [no_isolation_option] + arguments = BaseCommand.arguments + [lockfile_option, no_isolation_option] def add_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument( diff --git a/pdm/cli/commands/remove.py b/pdm/cli/commands/remove.py index fde51f24a8..8dfd65dd59 100644 --- a/pdm/cli/commands/remove.py +++ b/pdm/cli/commands/remove.py @@ -2,7 +2,7 @@ from pdm.cli import actions from pdm.cli.commands.base import BaseCommand -from pdm.cli.options import dry_run_option, install_group +from pdm.cli.options import dry_run_option, install_group, lockfile_option from pdm.project import Project @@ -12,6 +12,7 @@ class Command(BaseCommand): def add_arguments(self, parser: argparse.ArgumentParser) -> None: install_group.add_to_parser(parser) dry_run_option.add_to_parser(parser) + lockfile_option.add_to_parser(parser) parser.add_argument( "-d", "--dev", diff --git a/pdm/cli/commands/sync.py b/pdm/cli/commands/sync.py index 76a4fbbb73..ca90f6dc2f 100644 --- a/pdm/cli/commands/sync.py +++ b/pdm/cli/commands/sync.py @@ -2,7 +2,13 @@ from pdm.cli import actions from pdm.cli.commands.base import BaseCommand -from pdm.cli.options import clean_group, dry_run_option, groups_group, install_group +from pdm.cli.options import ( + clean_group, + dry_run_option, + groups_group, + install_group, + lockfile_option, +) from pdm.project import Project @@ -12,6 +18,7 @@ class Command(BaseCommand): def add_arguments(self, parser: argparse.ArgumentParser) -> None: groups_group.add_to_parser(parser) dry_run_option.add_to_parser(parser) + lockfile_option.add_to_parser(parser) parser.add_argument( "-r", "--reinstall", diff --git a/pdm/cli/commands/update.py b/pdm/cli/commands/update.py index a6e03c9cef..0a73aaf965 100644 --- a/pdm/cli/commands/update.py +++ b/pdm/cli/commands/update.py @@ -5,6 +5,7 @@ from pdm.cli.options import ( groups_group, install_group, + lockfile_option, prerelease_option, save_strategy_group, unconstrained_option, @@ -19,6 +20,7 @@ class Command(BaseCommand): arguments = BaseCommand.arguments + [ groups_group, install_group, + lockfile_option, save_strategy_group, update_strategy_group, prerelease_option, diff --git a/pdm/cli/completions/pdm.bash b/pdm/cli/completions/pdm.bash index eee83d7253..47252eb840 100644 --- a/pdm/cli/completions/pdm.bash +++ b/pdm/cli/completions/pdm.bash @@ -29,7 +29,7 @@ _pdm_a919b69078acdf0a_complete() case "$com" in (add) - opts="--dev --dry-run --editable --global --group --help --no-editable --no-isolation --no-self --no-sync --prerelease --project --save-compatible --save-exact --save-minimum --save-wildcard --unconstrained --update-eager --update-reuse --verbose" + opts="--dev --dry-run --editable --global --group --help --lockfile --no-editable --no-isolation --no-self --no-sync --prerelease --project --save-compatible --save-exact --save-minimum --save-wildcard --unconstrained --update-eager --update-reuse --verbose" ;; (build) @@ -49,7 +49,7 @@ _pdm_a919b69078acdf0a_complete() ;; (export) - opts="--dev --format --global --group --help --no-default --output --production --project --pyproject --verbose --without-hashes" + opts="--dev --format --global --group --help --lockfile --no-default --output --production --project --pyproject --verbose --without-hashes" ;; (import) @@ -65,7 +65,7 @@ _pdm_a919b69078acdf0a_complete() ;; (install) - opts="--check --dev --dry-run --global --group --help --no-default --no-editable --no-isolation --no-lock --no-self --production --project --verbose" + opts="--check --dev --dry-run --global --group --help --lockfile --no-default --no-editable --no-isolation --no-lock --no-self --production --project --verbose" ;; (list) @@ -73,7 +73,7 @@ _pdm_a919b69078acdf0a_complete() ;; (lock) - opts="--global --help --no-isolation --project --refresh --verbose" + opts="--global --help --lockfile --no-isolation --project --refresh --verbose" ;; (plugin) @@ -81,7 +81,7 @@ _pdm_a919b69078acdf0a_complete() ;; (remove) - opts="--dev --dry-run --global --group --help --no-editable --no-isolation --no-self --no-sync --project --verbose" + opts="--dev --dry-run --global --group --help --lockfile --no-editable --no-isolation --no-self --no-sync --project --verbose" ;; (run) @@ -97,11 +97,11 @@ _pdm_a919b69078acdf0a_complete() ;; (sync) - opts="--clean --dev --dry-run --global --group --help --no-clean --no-default --no-editable --no-isolation --no-self --production --project --reinstall --verbose" + opts="--clean --dev --dry-run --global --group --help --lockfile --no-clean --no-default --no-editable --no-isolation --no-self --production --project --reinstall --verbose" ;; (update) - opts="--dev --global --group --help --no-default --no-editable --no-isolation --no-self --no-sync --outdated --prerelease --production --project --save-compatible --save-exact --save-minimum --save-wildcard --top --unconstrained --update-eager --update-reuse --verbose" + opts="--dev --global --group --help --lockfile --no-default --no-editable --no-isolation --no-self --no-sync --outdated --prerelease --production --project --save-compatible --save-exact --save-minimum --save-wildcard --top --unconstrained --update-eager --update-reuse --verbose" ;; (use) diff --git a/pdm/cli/completions/pdm.fish b/pdm/cli/completions/pdm.fish index 33a256ff53..34e34499d3 100644 --- a/pdm/cli/completions/pdm.fish +++ b/pdm/cli/completions/pdm.fish @@ -49,6 +49,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from add' -l editable -d 'Specify complete -c pdm -A -n '__fish_seen_subcommand_from add' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l group -d 'Specify the target dependency group to add into' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from add' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-editable -d 'Install non-editable versions for all packages' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-isolation -d 'Do not isolate the build in a clean environment' complete -c pdm -A -n '__fish_seen_subcommand_from add' -l no-self -d 'Don\'t install the project itself' @@ -96,6 +97,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from export' -l format -d 'Specify complete -c pdm -A -n '__fish_seen_subcommand_from export' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l group -d 'Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from export' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l output -d 'Write output to the given file, or print to stdout if not given' complete -c pdm -A -n '__fish_seen_subcommand_from export' -l production -d 'Unselect dev dependencies' @@ -137,6 +139,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from install' -l dry-run -d 'Show complete -c pdm -A -n '__fish_seen_subcommand_from install' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l group -d 'Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from install' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-editable -d 'Install non-editable versions for all packages' complete -c pdm -A -n '__fish_seen_subcommand_from install' -l no-isolation -d 'Do not isolate the build in a clean environment' @@ -159,6 +162,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from list' -l verbose -d '-v for d # lock complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l no-isolation -d 'Do not isolate the build in a clean environment' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l project -d 'Specify another path as the project root, which changes the base of pyproject.toml and __pypackages__' complete -c pdm -A -n '__fish_seen_subcommand_from lock' -l refresh -d 'Don\'t update pinned versions, only refresh the lock file' @@ -174,6 +178,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l dry-run -d 'Show t complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l group -d 'Specify the target dependency group to remove from' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-editable -d 'Install non-editable versions for all packages' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-isolation -d 'Do not isolate the build in a clean environment' complete -c pdm -A -n '__fish_seen_subcommand_from remove' -l no-self -d 'Don\'t install the project itself' @@ -212,6 +217,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l dry-run -d 'Show the complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l group -d 'Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-clean -d 'don\'t clean unused packages' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from sync' -l no-editable -d 'Install non-editable versions for all packages' @@ -227,6 +233,7 @@ complete -c pdm -A -n '__fish_seen_subcommand_from update' -l dev -d 'Select dev complete -c pdm -A -n '__fish_seen_subcommand_from update' -l global -d 'Use the global project, supply the project root with `-p` option' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l group -d 'Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species.' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l help -d 'show this help message and exit' +complete -c pdm -A -n '__fish_seen_subcommand_from update' -l lockfile -d 'Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-default -d 'Don\'t include dependencies from the default group' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-editable -d 'Install non-editable versions for all packages' complete -c pdm -A -n '__fish_seen_subcommand_from update' -l no-isolation -d 'Do not isolate the build in a clean environment' diff --git a/pdm/cli/completions/pdm.ps1 b/pdm/cli/completions/pdm.ps1 index 06cfd74c2e..003719cdd3 100644 --- a/pdm/cli/completions/pdm.ps1 +++ b/pdm/cli/completions/pdm.ps1 @@ -198,7 +198,7 @@ function TabExpansion($line, $lastWord) { "add" { $completer.AddOpts(@( - [Option]::new(("-d", "--dev", "--save-compatible", "--save-wildcard", "--dry-run", "--save-exact", "--save-minimum", "--update-eager", "--update-reuse", "-g", "--global", "--no-sync", "--no-editable", "--no-self", "-u", "--unconstrained", "--no-isolation", "--pre", "--prerelease")), + [Option]::new(("-d", "--dev", "--save-compatible", "--save-wildcard", "--dry-run", "--save-exact", "--save-minimum", "--update-eager", "--update-reuse", "-g", "--global", "--no-sync", "--no-editable", "--no-self", "-u", "--unconstrained", "--no-isolation", "--pre", "--prerelease", "-L", "--lockfile")), $sectionOption, $projectOption, [Option]::new(@("-e", "--editable")).WithValues(@(getPyPIPackages)) @@ -230,7 +230,7 @@ function TabExpansion($line, $lastWord) { } "export" { $completer.AddOpts(@( - [Option]::new(@("--dev", "--output", "--global", "--no-default", "--prod", "--production", "-g", "-d", "-o", "--without-hashes")), + [Option]::new(@("--dev", "--output", "--global", "--no-default", "--prod", "--production", "-g", "-d", "-o", "--without-hashes", "-L", "--lockfile")), $formatOption, $sectionOption, $projectOption @@ -264,7 +264,7 @@ function TabExpansion($line, $lastWord) { } "install" { $completer.AddOpts(@( - [Option]::new(("-d", "--dev", "-g", "--global", "--dry-run", "--no-default", "--no-lock", "--prod", "--production", "--no-editable", "--no-self", "--no-isolation", "--check")), + [Option]::new(("-d", "--dev", "-g", "--global", "--dry-run", "--no-default", "--no-lock", "--prod", "--production", "--no-editable", "--no-self", "--no-isolation", "--check", "-L", "--lockfile")), $sectionOption, $projectOption )) @@ -281,7 +281,7 @@ function TabExpansion($line, $lastWord) { "lock" { $completer.AddOpts( @( - [Option]::new(@("--global", "-g", "--no-isolation", "--refresh")), + [Option]::new(@("--global", "-g", "--no-isolation", "--refresh", "-L", "--lockfile")), $projectOption )) break @@ -310,7 +310,7 @@ function TabExpansion($line, $lastWord) { "remove" { $completer.AddOpts( @( - [Option]::new(@("--global", "-g", "--dev", "-d", "--dry-run", "--no-sync", "--no-editable", "--no-self", "--no-isolation")), + [Option]::new(@("--global", "-g", "--dev", "-d", "--dry-run", "--no-sync", "--no-editable", "--no-self", "--no-isolation", "-L", "--lockfile")), $projectOption, $sectionOption )) @@ -337,7 +337,7 @@ function TabExpansion($line, $lastWord) { } "sync" { $completer.AddOpts(@( - [Option]::new(("-d", "--dev", "-g", "--global", "--no-default", "--clean", "--no-clean", "--dry-run", "-r", "--reinstall", "--prod", "--production", "--no-editable", "--no-self", "--no-isolation")), + [Option]::new(("-d", "--dev", "-g", "--global", "--no-default", "--clean", "--no-clean", "--dry-run", "-r", "--reinstall", "--prod", "--production", "--no-editable", "--no-self", "--no-isolation", "-L", "--lockfile")), $sectionOption, $projectOption )) @@ -345,7 +345,7 @@ function TabExpansion($line, $lastWord) { } "update" { $completer.AddOpts(@( - [Option]::new(("-d", "--dev", "--save-compatible", "--prod", "--production", "--save-wildcard", "--save-exact", "--save-minimum", "--update-eager", "--update-reuse", "-g", "--global", "--dry-run", "--outdated", "--top", "-u", "--unconstrained", "--no-editable", "--no-self", "--no-isolation", "--no-sync", "--pre", "--prerelease")), + [Option]::new(("-d", "--dev", "--save-compatible", "--prod", "--production", "--save-wildcard", "--save-exact", "--save-minimum", "--update-eager", "--update-reuse", "-g", "--global", "--dry-run", "--outdated", "--top", "-u", "--unconstrained", "--no-editable", "--no-self", "--no-isolation", "--no-sync", "--pre", "--prerelease", "-L", "--lockfile")), $sectionOption, $projectOption )) diff --git a/pdm/cli/completions/pdm.zsh b/pdm/cli/completions/pdm.zsh index 0d1b62b8ec..4520aa7b5d 100644 --- a/pdm/cli/completions/pdm.zsh +++ b/pdm/cli/completions/pdm.zsh @@ -59,6 +59,7 @@ _pdm() { {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-d,--dev}'[Add packages into dev dependencies]' {-G,--group}'[Specify the target dependency group to add into]:group:_pdm_groups' + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' '--no-sync[Only write pyproject.toml and do not sync the working set]' '--save-compatible[Save compatible version specifiers]' '--save-wildcard[Save wildcard version specifiers]' @@ -136,6 +137,7 @@ _pdm() { {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-f+,--format+}"[Specify the export file format]:format:(pipfile poetry flit requirements setuppy)" "--without-hashes[Don't include artifact hashes]" + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {-o+,--output+}"[Write output to the given file, or print to stdout if not given]:output file:_files" {-G+,--group+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' {-d,--dev}"[Select dev dependencies]" @@ -170,6 +172,7 @@ _pdm() { {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' {-d,--dev}"[Select dev dependencies]" + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {--prod,--production}"[Unselect dev dependencies]" "--no-lock[Don't do lock if lock file is not found or outdated]" "--no-default[Don\'t include dependencies from the default group]" @@ -192,6 +195,7 @@ _pdm() { lock) arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--no-isolation[Do not isolate the build in a clean environment]" "--refresh[Don't update pinned versions, only refresh the lock file]" ) @@ -237,6 +241,7 @@ _pdm() { {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G,--group}'[Specify the target dependency group to remove from]:group:_pdm_groups' {-d,--dev}"[Remove packages from dev dependencies]" + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' "--no-sync[Only write pyproject.toml and do not uninstall packages]" '--no-editable[Install non-editable versions for all packages]' "--no-self[Don't install the project itself]" @@ -281,6 +286,7 @@ _pdm() { {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' {-d,--dev}"[Select dev dependencies]" + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' {--prod,--production}"[Unselect dev dependencies]" '--dry-run[Only prints actions without actually running them]' {-r,--reinstall}"[Force reinstall existing dependencies]" @@ -296,6 +302,7 @@ _pdm() { arguments+=( {-g,--global}'[Use the global project, supply the project root with `-p` option]' {-G+,--group+}'[Select group of optional-dependencies or dev-dependencies(with -d). Can be supplied multiple times, use ":all" to include all groups under the same species]:group:_pdm_groups' + {-L,--lockfile}'[Specify another lockfile path, or use `PDM_LOCKFILE` env variable. Default: pdm.lock]:lockfile:_files' '--save-compatible[Save compatible version specifiers]' '--save-wildcard[Save wildcard version specifiers]' '--save-exact[Save exact version specifiers]' diff --git a/pdm/cli/options.py b/pdm/cli/options.py index f821bf5da2..921780d835 100644 --- a/pdm/cli/options.py +++ b/pdm/cli/options.py @@ -112,6 +112,13 @@ def wrapped_type(obj: Any) -> Any: help="Show the difference only and don't perform any action", ) +lockfile_option = Option( + "-L", + "--lockfile", + default=os.getenv("PDM_LOCKFILE"), + help="Specify another lockfile path, or use `PDM_LOCKFILE` env variable. " + "Default: pdm.lock", +) pep582_option = Option( "--pep582", diff --git a/pdm/core.py b/pdm/core.py index 59bfc407d0..e9b78dc2e7 100644 --- a/pdm/core.py +++ b/pdm/core.py @@ -112,6 +112,8 @@ def ensure_project( global_config=options.config or os.getenv("PDM_CONFIG_FILE"), ) options.project = project + if getattr(options, "lockfile", None): + options.project.lockfile_file = options.lockfile def create_project( self, diff --git a/pdm/project/core.py b/pdm/project/core.py index 12eac2182d..b36c1b7536 100644 --- a/pdm/project/core.py +++ b/pdm/project/core.py @@ -103,6 +103,7 @@ def __init__( self.root = Path(root_path or "").absolute() self.is_global = is_global self.init_global_project() + self._lockfile_file = self.root / "pdm.lock" def __repr__(self) -> str: return f"" @@ -113,7 +114,12 @@ def pyproject_file(self) -> Path: @property def lockfile_file(self) -> Path: - return self.root / "pdm.lock" + return self._lockfile_file + + @lockfile_file.setter + def lockfile_file(self, path: str) -> None: + self._lockfile_file = Path(path).absolute() + self._lockfile = None @property def pyproject(self) -> dict | None: diff --git a/tests/cli/test_lock.py b/tests/cli/test_lock.py new file mode 100644 index 0000000000..e3eb48e50a --- /dev/null +++ b/tests/cli/test_lock.py @@ -0,0 +1,66 @@ +import pytest + +from pdm.cli import actions +from pdm.models.requirements import parse_requirement + + +def test_lock_command(project, invoke, mocker): + m = mocker.patch.object(actions, "do_lock") + invoke(["lock"], obj=project) + m.assert_called_with(project, refresh=False) + + +@pytest.mark.usefixtures("repository") +def test_lock_dependencies(project): + project.add_dependencies({"requests": parse_requirement("requests")}) + actions.do_lock(project) + assert project.lockfile_file.exists() + locked = project.locked_repository.all_candidates + for package in ("requests", "idna", "chardet", "certifi"): + assert package in locked + + +def test_lock_refresh(invoke, project, repository): + project.add_dependencies({"requests": parse_requirement("requests")}) + result = invoke(["lock"], obj=project) + assert result.exit_code == 0 + assert project.is_lockfile_hash_match() + assert not project.lockfile["metadata"]["files"].get("requests 2.19.1") + project.add_dependencies({"requests": parse_requirement("requests>=2.0")}) + repository.get_hashes = ( + lambda c: {"requests-2.19.1-py3-none-any.whl": "sha256:abcdef123456"} + if c.identify() == "requests" + else {} + ) + assert not project.is_lockfile_hash_match() + result = invoke(["lock", "--refresh"], obj=project) + assert result.exit_code == 0 + assert project.is_lockfile_hash_match() + assert project.lockfile["metadata"]["files"]["requests 2.19.1"][0] == { + "file": "requests-2.19.1-py3-none-any.whl", + "hash": "sha256:abcdef123456", + } + + +def test_lock_refresh_keep_consistent(invoke, project, repository): + project.add_dependencies({"requests": parse_requirement("requests")}) + result = invoke(["lock"], obj=project) + assert result.exit_code == 0 + assert project.is_lockfile_hash_match() + previous = project.lockfile_file.read_text() + result = invoke(["lock", "--refresh"], obj=project) + assert result.exit_code == 0 + assert project.lockfile_file.read_text() == previous + + +@pytest.mark.usefixtures("repository") +def test_innovations_with_specified_lockfile(invoke, project, working_set): + project.add_dependencies({"requests": parse_requirement("requests")}) + lockfile = str(project.root / "mylock.lock") + invoke(["lock", "--lockfile", lockfile], strict=True, obj=project) + assert project.lockfile_file == project.root / "mylock.lock" + assert project.is_lockfile_hash_match() + locked = project.locked_repository.all_candidates + assert "requests" in locked + invoke(["sync", "--lockfile", lockfile], strict=True, obj=project) + assert "requests" in working_set diff --git a/tests/cli/test_others.py b/tests/cli/test_others.py index 38403d7e43..f237328563 100644 --- a/tests/cli/test_others.py +++ b/tests/cli/test_others.py @@ -7,16 +7,6 @@ from tests import FIXTURES -@pytest.mark.usefixtures("repository") -def test_lock_dependencies(project): - project.add_dependencies({"requests": parse_requirement("requests")}) - actions.do_lock(project) - assert project.lockfile_file.exists() - locked = project.locked_repository.all_candidates - for package in ("requests", "idna", "chardet", "certifi"): - assert package in locked - - @pytest.mark.usefixtures("project_no_init", "local_finder") def test_build_distributions(tmp_path, core): project = core.create_project() @@ -51,12 +41,6 @@ def test_help_option(invoke): assert "PDM - Python Development Master" in result.output -def test_lock_command(project, invoke, mocker): - m = mocker.patch.object(actions, "do_lock") - invoke(["lock"], obj=project) - m.assert_called_with(project, refresh=False) - - def test_info_command(project, invoke): result = invoke(["info"], obj=project) assert "Project Root:" in result.output @@ -261,39 +245,6 @@ def test_completion_command(invoke): assert "(completion)" in result.output -def test_lock_refresh(invoke, project, repository): - project.add_dependencies({"requests": parse_requirement("requests")}) - result = invoke(["lock"], obj=project) - assert result.exit_code == 0 - assert project.is_lockfile_hash_match() - assert not project.lockfile["metadata"]["files"].get("requests 2.19.1") - project.add_dependencies({"requests": parse_requirement("requests>=2.0")}) - repository.get_hashes = ( - lambda c: {"requests-2.19.1-py3-none-any.whl": "sha256:abcdef123456"} - if c.identify() == "requests" - else {} - ) - assert not project.is_lockfile_hash_match() - result = invoke(["lock", "--refresh"], obj=project) - assert result.exit_code == 0 - assert project.is_lockfile_hash_match() - assert project.lockfile["metadata"]["files"]["requests 2.19.1"][0] == { - "file": "requests-2.19.1-py3-none-any.whl", - "hash": "sha256:abcdef123456", - } - - -def test_lock_refresh_keep_consistent(invoke, project, repository): - project.add_dependencies({"requests": parse_requirement("requests")}) - result = invoke(["lock"], obj=project) - assert result.exit_code == 0 - assert project.is_lockfile_hash_match() - previous = project.lockfile_file.read_text() - result = invoke(["lock", "--refresh"], obj=project) - assert result.exit_code == 0 - assert project.lockfile_file.read_text() == previous - - @pytest.mark.network def test_show_update_hint(invoke, project): prev_version = project.core.version