diff --git a/.flake8 b/.flake8 index a37d4d5db5..46776f0502 100644 --- a/.flake8 +++ b/.flake8 @@ -15,3 +15,6 @@ ignore = B008,B011, # flake8-bandit security warnings we disagree with or don't mind S101,S102,S105,S110,S307,S311,S404,S6 +select = + # enable checks for self-or-cls param name, use of raise-from + B902,B904 diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..eb46828fd5 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +This patch fixes some new linter warnings such as :pypi:`flake8-bugbear`'s +``B904`` for explicit exception chaining, so tracebacks might be a bit nicer. diff --git a/hypothesis-python/src/hypothesis/_settings.py b/hypothesis-python/src/hypothesis/_settings.py index 850fceba3c..5879625d19 100644 --- a/hypothesis-python/src/hypothesis/_settings.py +++ b/hypothesis-python/src/hypothesis/_settings.py @@ -92,11 +92,11 @@ def __doc__(self): class settingsMeta(type): - def __init__(self, *args, **kwargs): + def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) @property - def default(self): + def default(cls): v = default_variable.value if v is not None: return v @@ -105,10 +105,10 @@ def default(self): assert default_variable.value is not None return default_variable.value - def _assign_default_internal(self, value): + def _assign_default_internal(cls, value): default_variable.value = value - def __setattr__(self, name, value): + def __setattr__(cls, name, value): if name == "default": raise AttributeError( "Cannot assign to the property settings.default - " @@ -121,7 +121,7 @@ def __setattr__(self, name, value): "settings with settings.load_profile, or use @settings(...) " "to decorate your test instead." ) - return type.__setattr__(self, name, value) + return type.__setattr__(cls, name, value) class settings(metaclass=settingsMeta): diff --git a/hypothesis-python/src/hypothesis/core.py b/hypothesis-python/src/hypothesis/core.py index 8d972361d2..3a8ddb0ca6 100644 --- a/hypothesis-python/src/hypothesis/core.py +++ b/hypothesis-python/src/hypothesis/core.py @@ -206,15 +206,17 @@ def decode_failure(blob): try: buffer = base64.b64decode(blob) except Exception: - raise InvalidArgument(f"Invalid base64 encoded string: {blob!r}") + raise InvalidArgument(f"Invalid base64 encoded string: {blob!r}") from None prefix = buffer[:1] if prefix == b"\0": return buffer[1:] elif prefix == b"\1": try: return zlib.decompress(buffer[1:]) - except zlib.error: - raise InvalidArgument(f"Invalid zlib compression for blob {blob!r}") + except zlib.error as err: + raise InvalidArgument( + f"Invalid zlib compression for blob {blob!r}" + ) from err else: raise InvalidArgument( f"Could not decode blob {blob!r}: Invalid start byte {prefix!r}" @@ -724,7 +726,7 @@ def _execute_once_for_engine(self, data): # This can happen if an error occurred in a finally # block somewhere, suppressing our original StopTest. # We raise a new one here to resume normal operation. - raise StopTest(data.testcounter) + raise StopTest(data.testcounter) from e else: # The test failed by raising an exception, so we inform the # engine that this test run was interesting. This is the normal @@ -1107,13 +1109,13 @@ def wrapped_test(*arguments, **kwargs): "The shape of the test data has changed in some way " "from where this blob was defined. Are you sure " "you're running the same test?" - ) + ) from None except UnsatisfiedAssumption: raise DidNotReproduce( "The test data failed to satisfy an assumption in the " "test. Have you added it since this blob was " "generated?" - ) + ) from None # There was no @reproduce_failure, so start by running any explicit # examples from @example decorators. diff --git a/hypothesis-python/src/hypothesis/extra/cli.py b/hypothesis-python/src/hypothesis/extra/cli.py index 98b37fb340..fa6ed6de8a 100644 --- a/hypothesis-python/src/hypothesis/extra/cli.py +++ b/hypothesis-python/src/hypothesis/extra/cli.py @@ -96,21 +96,21 @@ def obj_name(s: str) -> object: modulename, funcname = s.rsplit(".", 1) try: module = importlib.import_module(modulename) - except ImportError: + except ImportError as err: raise click.UsageError( f"Failed to import the {modulename} module for introspection. " "Check spelling and your Python import path, or use the Python API?" - ) + ) from err try: return getattr(module, funcname) - except AttributeError: + except AttributeError as err: public_names = [name for name in vars(module) if not name.startswith("_")] matches = get_close_matches(funcname, public_names) raise click.UsageError( f"Found the {modulename!r} module, but it doesn't have a " f"{funcname!r} attribute." + (f" Closest matches: {matches!r}" if matches else "") - ) + ) from err def _refactor(func, fname): try: diff --git a/hypothesis-python/src/hypothesis/extra/lark.py b/hypothesis-python/src/hypothesis/extra/lark.py index dc89a7f7bf..bee7de6319 100644 --- a/hypothesis-python/src/hypothesis/extra/lark.py +++ b/hypothesis-python/src/hypothesis/extra/lark.py @@ -171,7 +171,7 @@ def draw_symbol(self, data, symbol, draw_state): "use of %%declare unless you pass `explicit`, a dict of " 'names-to-strategies, such as `{%r: st.just("")}`' % (symbol.name, symbol.name) - ) + ) from None draw_state.result.append(data.draw(strategy)) else: assert isinstance(symbol, NonTerminal) diff --git a/hypothesis-python/src/hypothesis/extra/pandas/impl.py b/hypothesis-python/src/hypothesis/extra/pandas/impl.py index cb242e6963..1dc69ed60c 100644 --- a/hypothesis-python/src/hypothesis/extra/pandas/impl.py +++ b/hypothesis-python/src/hypothesis/extra/pandas/impl.py @@ -96,11 +96,11 @@ def convert_element(value): raise InvalidArgument( "Cannot convert %s=%r of type %s to dtype %s" % (name, value, type(value).__name__, dtype.str) - ) + ) from None except ValueError: raise InvalidArgument( f"Cannot convert {name}={value!r} to type {dtype.str}" - ) + ) from None elements = elements.map(convert_element) assert elements is not None @@ -518,7 +518,7 @@ def row(): "Column names must be hashable, but columns[%d].name was " "%r of type %s, which cannot be hashed." % (i, c.name, type(c.name).__name__) - ) + ) from None if c.name in column_names: raise InvalidArgument(f"duplicate definition of column name {c.name!r}") diff --git a/hypothesis-python/src/hypothesis/internal/cache.py b/hypothesis-python/src/hypothesis/internal/cache.py index 71a4cb80b7..2abc158956 100644 --- a/hypothesis-python/src/hypothesis/internal/cache.py +++ b/hypothesis-python/src/hypothesis/internal/cache.py @@ -94,7 +94,7 @@ def __setitem__(self, key, value): if self.max_size == self.__pinned_entry_count: raise ValueError( "Cannot increase size of cache where all keys have been pinned." - ) + ) from None entry = Entry(key, value, self.new_entry(key, value)) if len(self.data) >= self.max_size: evicted = self.data[0] diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/data.py b/hypothesis-python/src/hypothesis/internal/conjecture/data.py index 7bb73e59e6..c85d68c2b6 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/data.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/data.py @@ -744,10 +744,8 @@ def as_result(self): class ConjectureData: @classmethod - def for_buffer(self, buffer, observer=None): - return ConjectureData( - prefix=buffer, max_length=len(buffer), random=None, observer=observer - ) + def for_buffer(cls, buffer, observer=None): + return cls(len(buffer), buffer, random=None, observer=observer) def __init__(self, max_length, prefix, random, observer=None): if observer is None: diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py b/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py index b10edad529..e8e85c7a46 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py @@ -298,8 +298,8 @@ def simulate_test_function(self, data): v = data.draw_bits(node.transition.bit_length) try: node = node.transition.children[v] - except KeyError: - raise PreviouslyUnseenBehaviour() + except KeyError as err: + raise PreviouslyUnseenBehaviour() from err else: assert isinstance(node.transition, Killed) data.observer.kill_branch() diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/junkdrawer.py b/hypothesis-python/src/hypothesis/internal/conjecture/junkdrawer.py index d1191d3a71..23f8f92766 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/junkdrawer.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/junkdrawer.py @@ -75,8 +75,8 @@ def __init__(self, values=()): raise ValueError(f"Could not create IntList for {values!r}") @classmethod - def of_length(self, n): - return IntList(array_or_list("B", [0]) * n) + def of_length(cls, n): + return cls(array_or_list("B", [0]) * n) def count(self, n): return self.__underlying.count(n) diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py index 9afe74f9f2..9e5861854e 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py @@ -246,7 +246,7 @@ class Shrinker: """ - def derived_value(fn): + def derived_value(fn): # noqa: B902 """It's useful during shrinking to have access to derived values of the current shrink target. diff --git a/hypothesis-python/src/hypothesis/internal/validation.py b/hypothesis-python/src/hypothesis/internal/validation.py index 17c8382438..06d20e9ce6 100644 --- a/hypothesis-python/src/hypothesis/internal/validation.py +++ b/hypothesis-python/src/hypothesis/internal/validation.py @@ -89,11 +89,11 @@ def try_convert(typ, value, name): return value try: return typ(value) - except (TypeError, ValueError, ArithmeticError): + except (TypeError, ValueError, ArithmeticError) as err: raise InvalidArgument( f"Cannot convert {name}={value!r} of type " f"{type(value).__name__} to type {typ.__name__}" - ) + ) from err @check_function diff --git a/hypothesis-python/src/hypothesis/stateful.py b/hypothesis-python/src/hypothesis/stateful.py index 18157d99b7..558bf13046 100644 --- a/hypothesis-python/src/hypothesis/stateful.py +++ b/hypothesis-python/src/hypothesis/stateful.py @@ -224,16 +224,16 @@ def run_state_machine(factory, data): class StateMachineMeta(type): - def __setattr__(self, name, value): + def __setattr__(cls, name, value): if name == "settings" and isinstance(value, Settings): raise AttributeError( ( "Assigning {cls}.settings = {value} does nothing. Assign " "to {cls}.TestCase.settings, or use @{value} as a decorator " "on the {cls} class." - ).format(cls=self.__name__, value=value) + ).format(cls=cls.__name__, value=value) ) - return type.__setattr__(self, name, value) + return type.__setattr__(cls, name, value) class RuleBasedStateMachine(metaclass=StateMachineMeta): @@ -388,19 +388,17 @@ def teardown(self): @classmethod @lru_cache() - def _to_test_case(state_machine_class): + def _to_test_case(cls): class StateMachineTestCase(TestCase): settings = Settings(deadline=None, suppress_health_check=HealthCheck.all()) def runTest(self): - run_state_machine_as_test(state_machine_class) + run_state_machine_as_test(cls) runTest.is_hypothesis_test = True - StateMachineTestCase.__name__ = state_machine_class.__name__ + ".TestCase" - StateMachineTestCase.__qualname__ = ( - state_machine_class.__qualname__ + ".TestCase" - ) + StateMachineTestCase.__name__ = cls.__name__ + ".TestCase" + StateMachineTestCase.__qualname__ = cls.__qualname__ + ".TestCase" return StateMachineTestCase diff --git a/hypothesis-python/tests/cover/test_composite.py b/hypothesis-python/tests/cover/test_composite.py index 4460c03647..0e3bb0c3cd 100644 --- a/hypothesis-python/tests/cover/test_composite.py +++ b/hypothesis-python/tests/cover/test_composite.py @@ -136,12 +136,12 @@ def strat(draw, arg): class ClsWithStrategyMethods: @classmethod @st.composite - def st_classmethod_then_composite(draw, cls): + def st_classmethod_then_composite(draw, cls): # noqa: B902 return draw(st.integers(0, 10)) @st.composite @classmethod - def st_composite_then_classmethod(draw, cls): + def st_composite_then_classmethod(draw, cls): # noqa: B902 return draw(st.integers(0, 10)) @staticmethod @@ -155,7 +155,7 @@ def st_composite_then_staticmethod(draw): return draw(st.integers(0, 10)) @st.composite - def st_composite_method(draw, self): + def st_composite_method(draw, self): # noqa: B902 return draw(st.integers(0, 10)) diff --git a/hypothesis-python/tests/cover/test_detection.py b/hypothesis-python/tests/cover/test_detection.py index 2881090882..5937926d7b 100644 --- a/hypothesis-python/tests/cover/test_detection.py +++ b/hypothesis-python/tests/cover/test_detection.py @@ -28,7 +28,7 @@ def foo(): def test_methods_default_to_not_tests(): class Foo: - def foo(): + def foo(self): pass assert not is_hypothesis_test(Foo().foo) diff --git a/hypothesis-python/tests/cover/test_flakiness.py b/hypothesis-python/tests/cover/test_flakiness.py index 37ac714dd2..757ea3e95b 100644 --- a/hypothesis-python/tests/cover/test_flakiness.py +++ b/hypothesis-python/tests/cover/test_flakiness.py @@ -126,4 +126,4 @@ def test(x): except (Nope, Unsatisfiable, Flaky): pass except UnsatisfiedAssumption: - raise SatisfyMe() + raise SatisfyMe() from None diff --git a/hypothesis-python/tests/cover/test_lookup.py b/hypothesis-python/tests/cover/test_lookup.py index 0692998b86..4acad73eb2 100644 --- a/hypothesis-python/tests/cover/test_lookup.py +++ b/hypothesis-python/tests/cover/test_lookup.py @@ -663,7 +663,7 @@ def test_supportsop_types_support_protocol(protocol, data): [ (typing.SupportsFloat, float), (typing.SupportsInt, int), - (typing.SupportsBytes, bytes), # noqa: B1 + (typing.SupportsBytes, bytes), (typing.SupportsComplex, complex), ], ) diff --git a/hypothesis-python/tests/cover/test_pretty.py b/hypothesis-python/tests/cover/test_pretty.py index f2dab4cc1d..3d6875c9d5 100644 --- a/hypothesis-python/tests/cover/test_pretty.py +++ b/hypothesis-python/tests/cover/test_pretty.py @@ -350,11 +350,11 @@ def test_unbound_method(): class MetaClass(type): - def __new__(cls, name): - return type.__new__(cls, name, (object,), {"name": name}) + def __new__(metacls, name): + return type.__new__(metacls, name, (object,), {"name": name}) - def __repr__(self): - return f"[CUSTOM REPR FOR CLASS {self.name}]" + def __repr__(cls): + return f"[CUSTOM REPR FOR CLASS {cls.name}]" ClassWithMeta = MetaClass("ClassWithMeta") diff --git a/hypothesis-python/tests/cover/test_settings.py b/hypothesis-python/tests/cover/test_settings.py index 7da8895c3e..f3effbd256 100644 --- a/hypothesis-python/tests/cover/test_settings.py +++ b/hypothesis-python/tests/cover/test_settings.py @@ -423,7 +423,7 @@ def test_assigning_to_settings_attribute_on_state_machine_raises_error(): class StateMachine(RuleBasedStateMachine): @rule(x=st.none()) - def a_rule(x): + def a_rule(self, x): assert x is None StateMachine.settings = settings() diff --git a/hypothesis-python/tests/cover/test_setup_teardown.py b/hypothesis-python/tests/cover/test_setup_teardown.py index 07515b15aa..102f533f5a 100644 --- a/hypothesis-python/tests/cover/test_setup_teardown.py +++ b/hypothesis-python/tests/cover/test_setup_teardown.py @@ -37,7 +37,7 @@ def give_me_an_int(self, x): pass @given(text()) - def give_me_a_string(myself, x): + def give_me_a_string(self, x): pass @given(integers()) diff --git a/hypothesis-python/tests/cover/test_testdecorators.py b/hypothesis-python/tests/cover/test_testdecorators.py index f7b14cdb55..08bb5a8c8a 100644 --- a/hypothesis-python/tests/cover/test_testdecorators.py +++ b/hypothesis-python/tests/cover/test_testdecorators.py @@ -116,7 +116,7 @@ def test_abs_non_negative_varargs_kwargs(self, *args, **kw): assert isinstance(self, TestCases) @given(x=integers()) - def test_abs_non_negative_varargs_kwargs_only(*args, **kw): + def test_abs_non_negative_varargs_kwargs_only(*args, **kw): # noqa: B902 assert abs(kw["x"]) >= 0 assert isinstance(args[0], TestCases) diff --git a/hypothesis-python/tests/nocover/test_build_signature.py b/hypothesis-python/tests/nocover/test_build_signature.py index e962cc892a..bfce6add1b 100644 --- a/hypothesis-python/tests/nocover/test_build_signature.py +++ b/hypothesis-python/tests/nocover/test_build_signature.py @@ -110,7 +110,7 @@ def test_build_with_non_types_in_signature(val): class UnconventionalSignature: - def __init__(x: int = 0, self: bool = True): + def __init__(x: int = 0, self: bool = True): # noqa: B902 assert not isinstance(x, int) x.self = self diff --git a/hypothesis-python/tests/nocover/test_interesting_origin.py b/hypothesis-python/tests/nocover/test_interesting_origin.py index 5074829439..3c1216de73 100644 --- a/hypothesis-python/tests/nocover/test_interesting_origin.py +++ b/hypothesis-python/tests/nocover/test_interesting_origin.py @@ -28,7 +28,7 @@ def go_wrong_naive(a, b): except Exception: # Hiding the actual problem is terrible, but this pattern can make sense # if you're raising a library-specific or semantically meaningful error. - raise ValueError("Something went wrong") + raise ValueError("Something went wrong") from None def go_wrong_with_cause(a, b): diff --git a/hypothesis-python/tests/nocover/test_strategy_state.py b/hypothesis-python/tests/nocover/test_strategy_state.py index 638cce3842..0d8265b017 100644 --- a/hypothesis-python/tests/nocover/test_strategy_state.py +++ b/hypothesis-python/tests/nocover/test_strategy_state.py @@ -107,7 +107,7 @@ def strategy_for_tupes(self, spec): return tuples(*spec) @rule(target=strategies, source=strategies, level=integers(1, 10), mixer=text()) - def filtered_strategy(s, source, level, mixer): + def filtered_strategy(self, source, level, mixer): def is_good(x): seed = hashlib.sha384((mixer + repr(x)).encode()).digest() return bool(Random(seed).randint(0, level))