@@ -13,6 +13,8 @@ pub enum RequirementsSource {
1313 Package ( RequirementsTxtRequirement ) ,
1414 /// An editable path was provided on the command line (e.g., `pip install -e ../flask`).
1515 Editable ( RequirementsTxtRequirement ) ,
16+ /// Dependencies were provided via a `pylock.toml` file.
17+ PylockToml ( PathBuf ) ,
1618 /// Dependencies were provided via a `requirements.txt` file (e.g., `pip install -r requirements.txt`).
1719 RequirementsTxt ( PathBuf ) ,
1820 /// Dependencies were provided via a `pyproject.toml` file (e.g., `pip-compile pyproject.toml`).
@@ -27,6 +29,12 @@ pub enum RequirementsSource {
2729 EnvironmentYml ( PathBuf ) ,
2830}
2931
32+ /// Returns `true` if a file name matches the `pylock.toml` pattern defined in PEP 751.
33+ #[ allow( clippy:: case_sensitive_file_extension_comparisons) ]
34+ fn is_pylock_toml ( file_name : & str ) -> bool {
35+ file_name. starts_with ( "pylock." ) && file_name. ends_with ( ".toml" )
36+ }
37+
3038impl RequirementsSource {
3139 /// Parse a [`RequirementsSource`] from a [`PathBuf`]. The file type is determined by the file
3240 /// extension.
@@ -39,19 +47,32 @@ impl RequirementsSource {
3947 Self :: SetupCfg ( path)
4048 } else if path. ends_with ( "environment.yml" ) {
4149 Self :: EnvironmentYml ( path)
50+ } else if path
51+ . file_name ( )
52+ . is_some_and ( |file_name| file_name. to_str ( ) . is_some_and ( is_pylock_toml) )
53+ {
54+ Self :: PylockToml ( path)
4255 } else {
4356 Self :: RequirementsTxt ( path)
4457 }
4558 }
4659
4760 /// Parse a [`RequirementsSource`] from a `requirements.txt` file.
4861 pub fn from_requirements_txt ( path : PathBuf ) -> Self {
49- for filename in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
50- if path. ends_with ( filename ) {
62+ for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
63+ if path. ends_with ( file_name ) {
5164 warn_user ! (
5265 "The file `{}` appears to be a `{}` file, but requirements must be specified in `requirements.txt` format." ,
5366 path. user_display( ) ,
54- filename
67+ file_name
68+ ) ;
69+ }
70+ }
71+ if let Some ( file_name) = path. file_name ( ) {
72+ if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
73+ warn_user ! (
74+ "The file `{}` appears to be a `pylock.toml` file, but requirements must be specified in `requirements.txt` format." ,
75+ path. user_display( ) ,
5576 ) ;
5677 }
5778 }
@@ -60,12 +81,20 @@ impl RequirementsSource {
6081
6182 /// Parse a [`RequirementsSource`] from a `constraints.txt` file.
6283 pub fn from_constraints_txt ( path : PathBuf ) -> Self {
63- for filename in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
64- if path. ends_with ( filename ) {
84+ for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
85+ if path. ends_with ( file_name ) {
6586 warn_user ! (
6687 "The file `{}` appears to be a `{}` file, but constraints must be specified in `requirements.txt` format." ,
6788 path. user_display( ) ,
68- filename
89+ file_name
90+ ) ;
91+ }
92+ }
93+ if let Some ( file_name) = path. file_name ( ) {
94+ if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
95+ warn_user ! (
96+ "The file `{}` appears to be a `pylock.toml` file, but constraints must be specified in `requirements.txt` format." ,
97+ path. user_display( ) ,
6998 ) ;
7099 }
71100 }
@@ -74,12 +103,20 @@ impl RequirementsSource {
74103
75104 /// Parse a [`RequirementsSource`] from an `overrides.txt` file.
76105 pub fn from_overrides_txt ( path : PathBuf ) -> Self {
77- for filename in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
78- if path. ends_with ( filename ) {
106+ for file_name in [ "pyproject.toml" , "setup.py" , "setup.cfg" ] {
107+ if path. ends_with ( file_name ) {
79108 warn_user ! (
80109 "The file `{}` appears to be a `{}` file, but overrides must be specified in `requirements.txt` format." ,
81110 path. user_display( ) ,
82- filename
111+ file_name
112+ ) ;
113+ }
114+ }
115+ if let Some ( file_name) = path. file_name ( ) {
116+ if file_name. to_str ( ) . is_some_and ( is_pylock_toml) {
117+ warn_user ! (
118+ "The file `{}` appears to be a `pylock.toml` file, but overrides must be specified in `requirements.txt` format." ,
119+ path. user_display( ) ,
83120 ) ;
84121 }
85122 }
@@ -110,7 +147,10 @@ impl RequirementsSource {
110147
111148 // Similarly, if the user provided a `pyproject.toml` file without `-r` (as in
112149 // `uv pip install pyproject.toml`), prompt them to correct it.
113- if ( name == "pyproject.toml" || name == "setup.py" || name == "setup.cfg" )
150+ if ( name == "pyproject.toml"
151+ || name == "setup.py"
152+ || name == "setup.cfg"
153+ || is_pylock_toml ( name) )
114154 && Path :: new ( & name) . is_file ( )
115155 {
116156 let term = Term :: stderr ( ) ;
@@ -155,7 +195,10 @@ impl RequirementsSource {
155195
156196 // Similarly, if the user provided a `pyproject.toml` file without `--with-requirements` (as in
157197 // `uvx --with pyproject.toml ruff`), prompt them to correct it.
158- if ( name == "pyproject.toml" || name == "setup.py" || name == "setup.cfg" )
198+ if ( name == "pyproject.toml"
199+ || name == "setup.py"
200+ || name == "setup.cfg"
201+ || is_pylock_toml ( name) )
159202 && Path :: new ( & name) . is_file ( )
160203 {
161204 let term = Term :: stderr ( ) ;
@@ -217,7 +260,8 @@ impl std::fmt::Display for RequirementsSource {
217260 match self {
218261 Self :: Package ( package) => write ! ( f, "{package:?}" ) ,
219262 Self :: Editable ( path) => write ! ( f, "-e {path:?}" ) ,
220- Self :: RequirementsTxt ( path)
263+ Self :: PylockToml ( path)
264+ | Self :: RequirementsTxt ( path)
221265 | Self :: PyprojectToml ( path)
222266 | Self :: SetupPy ( path)
223267 | Self :: SetupCfg ( path)
0 commit comments