mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-10-31 12:05:57 +00:00 
			
		
		
		
	[ty] Change environment.root to accept multiple paths (#18913)
				
					
				
			This commit is contained in:
		
							parent
							
								
									0194452928
								
							
						
					
					
						commit
						833be2e66a
					
				
					 4 changed files with 39 additions and 28 deletions
				
			
		
							
								
								
									
										11
									
								
								crates/ty/docs/configuration.md
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								crates/ty/docs/configuration.md
									
										
									
										generated
									
									
									
								
							|  | @ -134,9 +134,11 @@ python-version = "3.12" | ||||||
| 
 | 
 | ||||||
| #### `root` | #### `root` | ||||||
| 
 | 
 | ||||||
| The root of the project, used for finding first-party modules. | The root paths of the project, used for finding first-party modules. | ||||||
| 
 | 
 | ||||||
| If left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly: | Accepts a list of directory paths searched in priority order (first has highest priority). | ||||||
|  | 
 | ||||||
|  | If left unspecified, ty will try to detect common project layouts and initialize `root` accordingly: | ||||||
| 
 | 
 | ||||||
| * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) | * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) | ||||||
| * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path | * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path | ||||||
|  | @ -147,13 +149,14 @@ it will also be included in the first party search path. | ||||||
| 
 | 
 | ||||||
| **Default value**: `null` | **Default value**: `null` | ||||||
| 
 | 
 | ||||||
| **Type**: `str` | **Type**: `list[str]` | ||||||
| 
 | 
 | ||||||
| **Example usage** (`pyproject.toml`): | **Example usage** (`pyproject.toml`): | ||||||
| 
 | 
 | ||||||
| ```toml | ```toml | ||||||
| [tool.ty.environment] | [tool.ty.environment] | ||||||
| root = "./app" | # Multiple directories (priority order) | ||||||
|  | root = ["./src", "./lib", "./vendor"] | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -974,7 +974,7 @@ fn src_root_deprecation_warning_with_environment_root() -> anyhow::Result<()> { | ||||||
|             root = "./src" |             root = "./src" | ||||||
| 
 | 
 | ||||||
|             [tool.ty.environment] |             [tool.ty.environment] | ||||||
|             root = "./app" |             root = ["./app"] | ||||||
|             "#,
 |             "#,
 | ||||||
|         ), |         ), | ||||||
|         ("app/test.py", ""), |         ("app/test.py", ""), | ||||||
|  | @ -1014,7 +1014,7 @@ fn environment_root_takes_precedence_over_src_root() -> anyhow::Result<()> { | ||||||
|             root = "./src" |             root = "./src" | ||||||
| 
 | 
 | ||||||
|             [tool.ty.environment] |             [tool.ty.environment] | ||||||
|             root = "./app" |             root = ["./app"] | ||||||
|             "#,
 |             "#,
 | ||||||
|         ), |         ), | ||||||
|         ("src/test.py", "import my_module"), |         ("src/test.py", "import my_module"), | ||||||
|  |  | ||||||
|  | @ -146,13 +146,15 @@ impl Options { | ||||||
|         let src = self.src.or_default(); |         let src = self.src.or_default(); | ||||||
| 
 | 
 | ||||||
|         #[allow(deprecated)] |         #[allow(deprecated)] | ||||||
|         let src_roots = if let Some(src_root) = self |         let src_roots = if let Some(roots) = environment | ||||||
|             .environment |             .root | ||||||
|             .as_ref() |             .as_deref() | ||||||
|             .and_then(|environment| environment.root.as_ref()) |             .or_else(|| Some(std::slice::from_ref(src.root.as_ref()?))) | ||||||
|             .or_else(|| src.root.as_ref()) |  | ||||||
|         { |         { | ||||||
|             vec![src_root.absolute(project_root, system)] |             roots | ||||||
|  |                 .iter() | ||||||
|  |                 .map(|root| root.absolute(project_root, system)) | ||||||
|  |                 .collect() | ||||||
|         } else { |         } else { | ||||||
|             let src = project_root.join("src"); |             let src = project_root.join("src"); | ||||||
| 
 | 
 | ||||||
|  | @ -160,20 +162,20 @@ impl Options { | ||||||
|                 // Default to `src` and the project root if `src` exists and the root hasn't been specified.
 |                 // Default to `src` and the project root if `src` exists and the root hasn't been specified.
 | ||||||
|                 // This corresponds to the `src-layout`
 |                 // This corresponds to the `src-layout`
 | ||||||
|                 tracing::debug!( |                 tracing::debug!( | ||||||
|                     "Including `./src` in `src.root` because a `./src` directory exists" |                     "Including `.` and `./src` in `environment.root` because a `./src` directory exists" | ||||||
|                 ); |                 ); | ||||||
|                 vec![project_root.to_path_buf(), src] |                 vec![project_root.to_path_buf(), src] | ||||||
|             } else if system.is_directory(&project_root.join(project_name).join(project_name)) { |             } else if system.is_directory(&project_root.join(project_name).join(project_name)) { | ||||||
|                 // `src-layout` but when the folder isn't called `src` but has the same name as the project.
 |                 // `src-layout` but when the folder isn't called `src` but has the same name as the project.
 | ||||||
|                 // For example, the "src" folder for `psycopg` is called `psycopg` and the python files are in `psycopg/psycopg/_adapters_map.py`
 |                 // For example, the "src" folder for `psycopg` is called `psycopg` and the python files are in `psycopg/psycopg/_adapters_map.py`
 | ||||||
|                 tracing::debug!( |                 tracing::debug!( | ||||||
|                     "Including `./{project_name}` in `src.root` because a `./{project_name}/{project_name}` directory exists" |                     "Including `.` and `/{project_name}` in `environment.root` because a `./{project_name}/{project_name}` directory exists" | ||||||
|                 ); |                 ); | ||||||
| 
 | 
 | ||||||
|                 vec![project_root.to_path_buf(), project_root.join(project_name)] |                 vec![project_root.to_path_buf(), project_root.join(project_name)] | ||||||
|             } else { |             } else { | ||||||
|                 // Default to a [flat project structure](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).
 |                 // Default to a [flat project structure](https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/).
 | ||||||
|                 tracing::debug!("Defaulting `src.root` to `.`"); |                 tracing::debug!("Including `.` in `environment.root`"); | ||||||
|                 vec![project_root.to_path_buf()] |                 vec![project_root.to_path_buf()] | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|  | @ -186,7 +188,7 @@ impl Options { | ||||||
|             { |             { | ||||||
|                 // If the `tests` directory exists and is not a package, include it as a source root.
 |                 // If the `tests` directory exists and is not a package, include it as a source root.
 | ||||||
|                 tracing::debug!( |                 tracing::debug!( | ||||||
|                     "Including `./tests` in `src.root` because a `./tests` directory exists" |                     "Including `./tests` in `environment.root` because a `./tests` directory exists" | ||||||
|                 ); |                 ); | ||||||
| 
 | 
 | ||||||
|                 roots.push(tests_dir); |                 roots.push(tests_dir); | ||||||
|  | @ -248,7 +250,7 @@ impl Options { | ||||||
|         let mut diagnostics = Vec::new(); |         let mut diagnostics = Vec::new(); | ||||||
|         let rules = self.to_rule_selection(db, &mut diagnostics); |         let rules = self.to_rule_selection(db, &mut diagnostics); | ||||||
| 
 | 
 | ||||||
|         let terminal_options = self.terminal.clone().unwrap_or_default(); |         let terminal_options = self.terminal.or_default(); | ||||||
|         let terminal = TerminalSettings { |         let terminal = TerminalSettings { | ||||||
|             output_format: terminal_options |             output_format: terminal_options | ||||||
|                 .output_format |                 .output_format | ||||||
|  | @ -329,7 +331,7 @@ impl Options { | ||||||
|         project_root: &SystemPath, |         project_root: &SystemPath, | ||||||
|         diagnostics: &mut Vec<OptionDiagnostic>, |         diagnostics: &mut Vec<OptionDiagnostic>, | ||||||
|     ) -> Result<Vec<Override>, Box<OptionDiagnostic>> { |     ) -> Result<Vec<Override>, Box<OptionDiagnostic>> { | ||||||
|         let override_options = self.overrides.as_deref().unwrap_or_default(); |         let override_options = &**self.overrides.or_default(); | ||||||
| 
 | 
 | ||||||
|         let mut overrides = Vec::with_capacity(override_options.len()); |         let mut overrides = Vec::with_capacity(override_options.len()); | ||||||
| 
 | 
 | ||||||
|  | @ -352,9 +354,11 @@ impl Options { | ||||||
| #[serde(rename_all = "kebab-case", deny_unknown_fields)] | #[serde(rename_all = "kebab-case", deny_unknown_fields)] | ||||||
| #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] | #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] | ||||||
| pub struct EnvironmentOptions { | pub struct EnvironmentOptions { | ||||||
|     /// The root of the project, used for finding first-party modules.
 |     /// The root paths of the project, used for finding first-party modules.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// If left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly:
 |     /// Accepts a list of directory paths searched in priority order (first has highest priority).
 | ||||||
|  |     ///
 | ||||||
|  |     /// If left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)
 |     /// * if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat)
 | ||||||
|     /// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
 |     /// * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path
 | ||||||
|  | @ -365,12 +369,13 @@ pub struct EnvironmentOptions { | ||||||
|     #[serde(skip_serializing_if = "Option::is_none")] |     #[serde(skip_serializing_if = "Option::is_none")] | ||||||
|     #[option(
 |     #[option(
 | ||||||
|         default = r#"null"#, |         default = r#"null"#, | ||||||
|         value_type = "str", |         value_type = "list[str]", | ||||||
|         example = r#" |         example = r#" | ||||||
|             root = "./app" |             # Multiple directories (priority order) | ||||||
|  |             root = ["./src", "./lib", "./vendor"] | ||||||
|         "#
 |         "#
 | ||||||
|     )] |     )] | ||||||
|     pub root: Option<RelativePathBuf>, |     pub root: Option<Vec<RelativePathBuf>>, | ||||||
| 
 | 
 | ||||||
|     /// Specifies the version of Python that will be used to analyze the source code.
 |     /// Specifies the version of Python that will be used to analyze the source code.
 | ||||||
|     /// The version should be specified as a string in the format `M.m` where `M` is the major version
 |     /// The version should be specified as a string in the format `M.m` where `M` is the major version
 | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								ty.schema.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								ty.schema.json
									
										
									
										generated
									
									
									
								
							|  | @ -120,11 +120,14 @@ | ||||||
|           ] |           ] | ||||||
|         }, |         }, | ||||||
|         "root": { |         "root": { | ||||||
|           "description": "The root of the project, used for finding first-party modules.\n\nIf left unspecified, ty will try to detect common project layouts and initialize `src.root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path * otherwise, default to `.` (flat layout)\n\nBesides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file), it will also be included in the first party search path.", |           "description": "The root paths of the project, used for finding first-party modules.\n\nAccepts a list of directory paths searched in priority order (first has highest priority).\n\nIf left unspecified, ty will try to detect common project layouts and initialize `root` accordingly:\n\n* if a `./src` directory exists, include `.` and `./src` in the first party search path (src layout or flat) * if a `./<project-name>/<project-name>` directory exists, include `.` and `./<project-name>` in the first party search path * otherwise, default to `.` (flat layout)\n\nBesides, if a `./tests` directory exists and is not a package (i.e. it does not contain an `__init__.py` file), it will also be included in the first party search path.", | ||||||
|           "type": [ |           "type": [ | ||||||
|             "string", |             "array", | ||||||
|             "null" |             "null" | ||||||
|           ] |           ], | ||||||
|  |           "items": { | ||||||
|  |             "type": "string" | ||||||
|  |           } | ||||||
|         }, |         }, | ||||||
|         "typeshed": { |         "typeshed": { | ||||||
|           "description": "Optional path to a \"typeshed\" directory on disk for us to use for standard-library types. If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib, bundled as a zip file in the binary", |           "description": "Optional path to a \"typeshed\" directory on disk for us to use for standard-library types. If this is not provided, we will fallback to our vendored typeshed stubs for the stdlib, bundled as a zip file in the binary", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Micha Reiser
						Micha Reiser