Uploaded image for project: 'Rules Repository'
  1. Rules Repository
  2. RSPEC-5706

Special method "__exit__" should not re-raise the provided exception

    XMLWordPrintable

    Details

    • Message:
      remove this "raise" statement and return "False" instead.
    • Highlighting:
      Hide

      The "raise" statement.

      Show
      The "raise" statement.
    • Default Severity:
      Major
    • Impact:
      Low
    • Likelihood:
      High
    • Default Quality Profiles:
      Sonar way
    • Covered Languages:
      Python
    • Irrelevant for Languages:
      ABAP, APEX, C#, C, C++, Cobol, CSS, Flex, Go, HTML, Java, JavaScript, Kotlin, Objective-C, PHP, PL/I, PL/SQL, RPG, Ruby, Rust, Scala, Solidity, Swift, T-SQL, TypeScript, VB.Net, VB6, XML
    • Remediation Function:
      Constant/Issue
    • Constant Cost:
      30min
    • Analysis Scope:
      Main Sources, Test Sources

      Description

      The special method __exit__ should only raise an exception when it fails. It should never raise the provided exception, it is the caller's responsibility.
      Raising this exception will make the stack trace difficult to understand.

      The __exit__ method can filter passed-in exceptions by simply returning True or False.

      This rule raises an issue when:

      • an __exit__ method has a bare raise outside of an except block.
      • an __exit__ method raises the exception provided as parameter.

      Noncompliant Code Example

      class MyContextManager:
          def __enter__(self):
              return self
          def __exit__(self, *args):
              raise  # Noncompliant
              raise args[2]  # Noncompliant
      
      class MyContextManager:
          def __enter__(self):
              return self
          def __exit__(self, exc_type, exc_value, traceback):
              raise exc_value # Noncompliant
      

      Compliant Solution

      class MyContextManager:
          def __enter__(self):
              return self
          def __exit__(self, exc_type, exc_value, traceback):
              # by default the function will return None, which is always False, and the exc_value will naturally raise.
              pass
      
      class MyContextManager:
          def __enter__(self, stop_exceptions):
              return self
          def __exit__(self, *args):
              try:
                  print("42")
              except:
                  print("exception")
                  raise  # No issue when raising another exception. The __exit__ method can fail and raise an exception
              raise MemoryError("No more memory")  # This is ok too.
      

      See

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned Unassigned
              Reporter:
              nicolas.harraudeau Nicolas Harraudeau (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated: