mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-10-31 12:05:57 +00:00 
			
		
		
		
	[flake8-blind-except] Change BLE001 to correctly parse exception tuples (#19747)
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				CI / Determine changes (push) Waiting to run
				
			
		
			
				
	
				CI / cargo fmt (push) Waiting to run
				
			
		
			
				
	
				CI / cargo clippy (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (linux) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (linux, release) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (windows) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (wasm) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo build (release) (push) Waiting to run
				
			
		
			
				
	
				CI / cargo build (msrv) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo fuzz build (push) Blocked by required conditions
				
			
		
			
				
	
				CI / fuzz parser (push) Blocked by required conditions
				
			
		
			
				
	
				CI / test scripts (push) Blocked by required conditions
				
			
		
			
				
	
				CI / ecosystem (push) Blocked by required conditions
				
			
		
			
				
	
				CI / Fuzz for new ty panics (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo shear (push) Blocked by required conditions
				
			
		
			
				
	
				CI / python package (push) Waiting to run
				
			
		
			
				
	
				CI / pre-commit (push) Waiting to run
				
			
		
			
				
	
				CI / mkdocs (push) Waiting to run
				
			
		
			
				
	
				CI / formatter instabilities and black similarity (push) Blocked by required conditions
				
			
		
			
				
	
				CI / test ruff-lsp (push) Blocked by required conditions
				
			
		
			
				
	
				CI / check playground (push) Blocked by required conditions
				
			
		
			
				
	
				CI / benchmarks-instrumented (push) Blocked by required conditions
				
			
		
			
				
	
				CI / benchmarks-walltime (push) Blocked by required conditions
				
			
		
			
				
	
				[ty Playground] Release / publish (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	CI / Determine changes (push) Waiting to run
				
			CI / cargo fmt (push) Waiting to run
				
			CI / cargo clippy (push) Blocked by required conditions
				
			CI / cargo test (linux) (push) Blocked by required conditions
				
			CI / cargo test (linux, release) (push) Blocked by required conditions
				
			CI / cargo test (windows) (push) Blocked by required conditions
				
			CI / cargo test (wasm) (push) Blocked by required conditions
				
			CI / cargo build (release) (push) Waiting to run
				
			CI / cargo build (msrv) (push) Blocked by required conditions
				
			CI / cargo fuzz build (push) Blocked by required conditions
				
			CI / fuzz parser (push) Blocked by required conditions
				
			CI / test scripts (push) Blocked by required conditions
				
			CI / ecosystem (push) Blocked by required conditions
				
			CI / Fuzz for new ty panics (push) Blocked by required conditions
				
			CI / cargo shear (push) Blocked by required conditions
				
			CI / python package (push) Waiting to run
				
			CI / pre-commit (push) Waiting to run
				
			CI / mkdocs (push) Waiting to run
				
			CI / formatter instabilities and black similarity (push) Blocked by required conditions
				
			CI / test ruff-lsp (push) Blocked by required conditions
				
			CI / check playground (push) Blocked by required conditions
				
			CI / benchmarks-instrumented (push) Blocked by required conditions
				
			CI / benchmarks-walltime (push) Blocked by required conditions
				
			[ty Playground] Release / publish (push) Waiting to run
				
			## Summary
This PR enhances the `BLE001` rule to correctly detect blind exception
handling in tuple exceptions. Previously, the rule only checked single
exception types, but Python allows catching multiple exceptions using
tuples like `except (Exception, ValueError):`.
## Test Plan
It fails the following (whereas the main branch does not):
```bash
cargo run -p ruff -- check somefile.py --no-cache --select=BLE001
```
```python
# somefile.py
try:
    1/0
except (ValueError, Exception) as e:
    print(e)
```
```
somefile.py:3:21: BLE001 Do not catch blind exception: `Exception`
  |
1 | try:
2 |     1/0
3 | except (ValueError, Exception) as e:
  |                     ^^^^^^^^^ BLE001
4 |     print(e)
  |
Found 1 error.
```
			
			
This commit is contained in:
		
							parent
							
								
									3a9341f7be
								
							
						
					
					
						commit
						b0f01ba514
					
				
					 3 changed files with 192 additions and 6 deletions
				
			
		|  | @ -162,3 +162,86 @@ except Exception: | ||||||
|         exception("An error occurred") |         exception("An error occurred") | ||||||
|     else: |     else: | ||||||
|         exception("An error occurred") |         exception("An error occurred") | ||||||
|  | 
 | ||||||
|  | # Test tuple exceptions | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception,): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception, ValueError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (ValueError, Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (ValueError, Exception) as e: | ||||||
|  |     print(e) | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (BaseException, TypeError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (TypeError, BaseException): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception, BaseException): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (BaseException, Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | # Test nested tuples | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except ((Exception, ValueError), TypeError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (ValueError, (BaseException, TypeError)): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | # Test valid tuple exceptions (should not trigger) | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (ValueError, TypeError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (OSError, FileNotFoundError): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (OSError, FileNotFoundError) as e: | ||||||
|  |     print(e) | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception, ValueError): | ||||||
|  |     critical("...", exc_info=True) | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception, ValueError): | ||||||
|  |     raise | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     pass | ||||||
|  | except (Exception, ValueError) as e: | ||||||
|  |     raise e | ||||||
|  |  | ||||||
|  | @ -75,6 +75,22 @@ impl Violation for BlindExcept { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn contains_blind_exception<'a>( | ||||||
|  |     semantic: &'a SemanticModel, | ||||||
|  |     expr: &'a Expr, | ||||||
|  | ) -> Option<(&'a str, ruff_text_size::TextRange)> { | ||||||
|  |     match expr { | ||||||
|  |         Expr::Tuple(ast::ExprTuple { elts, .. }) => elts | ||||||
|  |             .iter() | ||||||
|  |             .find_map(|elt| contains_blind_exception(semantic, elt)), | ||||||
|  |         _ => { | ||||||
|  |             let builtin_exception_type = semantic.resolve_builtin_symbol(expr)?; | ||||||
|  |             matches!(builtin_exception_type, "BaseException" | "Exception") | ||||||
|  |                 .then(|| (builtin_exception_type, expr.range())) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// BLE001
 | /// BLE001
 | ||||||
| pub(crate) fn blind_except( | pub(crate) fn blind_except( | ||||||
|     checker: &Checker, |     checker: &Checker, | ||||||
|  | @ -87,12 +103,9 @@ pub(crate) fn blind_except( | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let semantic = checker.semantic(); |     let semantic = checker.semantic(); | ||||||
|     let Some(builtin_exception_type) = semantic.resolve_builtin_symbol(type_) else { |     let Some((builtin_exception_type, range)) = contains_blind_exception(semantic, type_) else { | ||||||
|         return; |         return; | ||||||
|     }; |     }; | ||||||
|     if !matches!(builtin_exception_type, "BaseException" | "Exception") { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     // If the exception is re-raised, don't flag an error.
 |     // If the exception is re-raised, don't flag an error.
 | ||||||
|     let mut visitor = ReraiseVisitor::new(name); |     let mut visitor = ReraiseVisitor::new(name); | ||||||
|  | @ -110,9 +123,9 @@ pub(crate) fn blind_except( | ||||||
| 
 | 
 | ||||||
|     checker.report_diagnostic( |     checker.report_diagnostic( | ||||||
|         BlindExcept { |         BlindExcept { | ||||||
|             name: builtin_exception_type.to_string(), |             name: builtin_exception_type.into(), | ||||||
|         }, |         }, | ||||||
|         type_.range(), |         range, | ||||||
|     ); |     ); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -147,3 +147,93 @@ BLE.py:131:8: BLE001 Do not catch blind exception: `Exception` | ||||||
|     |        ^^^^^^^^^ BLE001 |     |        ^^^^^^^^^ BLE001 | ||||||
| 132 |     critical("...", exc_info=None) | 132 |     critical("...", exc_info=None) | ||||||
|     | |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:169:9: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 167 | try: | ||||||
|  | 168 |     pass | ||||||
|  | 169 | except (Exception,): | ||||||
|  |     |         ^^^^^^^^^ BLE001 | ||||||
|  | 170 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:174:9: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 172 | try: | ||||||
|  | 173 |     pass | ||||||
|  | 174 | except (Exception, ValueError): | ||||||
|  |     |         ^^^^^^^^^ BLE001 | ||||||
|  | 175 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:179:21: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 177 | try: | ||||||
|  | 178 |     pass | ||||||
|  | 179 | except (ValueError, Exception): | ||||||
|  |     |                     ^^^^^^^^^ BLE001 | ||||||
|  | 180 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:184:21: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 182 | try: | ||||||
|  | 183 |     pass | ||||||
|  | 184 | except (ValueError, Exception) as e: | ||||||
|  |     |                     ^^^^^^^^^ BLE001 | ||||||
|  | 185 |     print(e) | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:189:9: BLE001 Do not catch blind exception: `BaseException` | ||||||
|  |     | | ||||||
|  | 187 | try: | ||||||
|  | 188 |     pass | ||||||
|  | 189 | except (BaseException, TypeError): | ||||||
|  |     |         ^^^^^^^^^^^^^ BLE001 | ||||||
|  | 190 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:194:20: BLE001 Do not catch blind exception: `BaseException` | ||||||
|  |     | | ||||||
|  | 192 | try: | ||||||
|  | 193 |     pass | ||||||
|  | 194 | except (TypeError, BaseException): | ||||||
|  |     |                    ^^^^^^^^^^^^^ BLE001 | ||||||
|  | 195 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:199:9: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 197 | try: | ||||||
|  | 198 |     pass | ||||||
|  | 199 | except (Exception, BaseException): | ||||||
|  |     |         ^^^^^^^^^ BLE001 | ||||||
|  | 200 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:204:9: BLE001 Do not catch blind exception: `BaseException` | ||||||
|  |     | | ||||||
|  | 202 | try: | ||||||
|  | 203 |     pass | ||||||
|  | 204 | except (BaseException, Exception): | ||||||
|  |     |         ^^^^^^^^^^^^^ BLE001 | ||||||
|  | 205 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:210:10: BLE001 Do not catch blind exception: `Exception` | ||||||
|  |     | | ||||||
|  | 208 | try: | ||||||
|  | 209 |     pass | ||||||
|  | 210 | except ((Exception, ValueError), TypeError): | ||||||
|  |     |          ^^^^^^^^^ BLE001 | ||||||
|  | 211 |     pass | ||||||
|  |     | | ||||||
|  | 
 | ||||||
|  | BLE.py:215:22: BLE001 Do not catch blind exception: `BaseException` | ||||||
|  |     | | ||||||
|  | 213 | try: | ||||||
|  | 214 |     pass | ||||||
|  | 215 | except (ValueError, (BaseException, TypeError)): | ||||||
|  |     |                      ^^^^^^^^^^^^^ BLE001 | ||||||
|  | 216 |     pass | ||||||
|  |     | | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Roman Kitaev
						Roman Kitaev