fixhunt

ruff PLW1507

labelled unknown
Runs / 2026-10-17T21-08-16-178Z-c9dh-ruff-PLW1507ruff 0.17.12026-10-17 21:08 UTC8 programs × 2 round(s)1 programs, 0 fired, 0 fixed, 0 findings, 0 rejectedmodel cost $0.0036

Analysis Nemotron 3 Super

Flags shallow copying of os.environ using copy.copy() due to mutation risks.

Fix: Replaces 'copy.copy(os.environ)' with 'os.environ.copy()' while preserving imports and surrounding context.

12 hazards
  • Rewriting inside a function call argument like f(copy.copy(os.environ)) changes evaluation timing if os.environ.copy() has side effects (though it doesn't, the form differs).
  • If 'copy' is locally rebound (e.g., 'copy = lambda x: x'), the fix incorrectly removes the call to the local 'copy'.
  • In a comprehension like [copy.copy(os.environ) for _ in range(1)], the fix alters iteration behavior if os.environ.copy() is expensive or has side effects (it doesn't, but structurally differs).
  • When the copy occurs in a lambda default argument like lambda x=copy.copy(os.environ): x, the fix changes when the copy is evaluated (at definition vs call time).
  • If the code relies on the type of the copy being exactly copy._wrap type (not dict), switching to dict breaks isinstance checks against copy.copy's result type.
  • In a try/except block where copy.copy might raise TypeError (if os.environ were not copyable, though it is), the fix removes the exception handling path.
  • If os.environ is shadowed by a local variable before the copy, the fix still uses the global os.environ, breaking intended behavior.
  • The fix may disrupt formatting-dependent tooling (e.g., linters that rely on line length) by replacing a longer expression with a shorter one.
  • If the original code had a comment between 'copy.' and 'copy(os.environ)', the fix removes it during AST rewrite.
  • In an augmented assignment like x += copy.copy(os.environ), the fix changes the right-hand side evaluation, potentially altering side effects if os.environ.copy() had them (it doesn't, but the form is different).
  • When used in a context manager like with lock: copy.copy(os.environ), the fix does not affect the lock but changes the expression value, possibly breaking downstream logic expecting a copy proxy.
  • If the module defines a class named 'environ' in os, the fix incorrectly attributes the copy to that instead of the real os.environ (though unlikely, depends on imports).

Programs

idresulttitlehazard
r2-p005silent prediction offEvaluation Order and Side Effects in Merged ExpressionsEvaluation order and side effects when expressions are merged, duplicated or deleted

Sources

r2-p005 · Evaluation Order and Side Effects in Merged Expressions
1import copy
2import os
3state = []
4class Track:
5 def __init__(self, name):
6 self.name = name
7 def __copy__(self):
8 state.append(f'copied {self.name}')
9 return self
10 def copy(self):
11 state.append(f'.copy {self.name}')
12 return self
13a = Track('a')
14b = Track('b')
15result = copy.copy(a) if state.append('eval') or True else b
16print('state:', state)
17print('result name:', result.name)
state: ['eval', 'copied a']
result name: a