Skip to content

Commit 2bf4c32

Browse files
committed
Python: Add syntactic support for yield in contextlib.contextmanager
1 parent 2399793 commit 2bf4c32

File tree

3 files changed

+31
-4
lines changed

3 files changed

+31
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added support for functions decorated with `contextlib.contextmanager`.

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,19 @@ predicate hasPropertyDecorator(Function func) {
240240
)
241241
}
242242

243+
/**
244+
* Holds if the function `func` has a `contextlib.contextmanager`.
245+
*/
246+
predicate hasContextmanagerDecorator(Function func) {
247+
exists(ControlFlowNode contextmanager |
248+
contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
249+
or
250+
contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
251+
|
252+
func.getADecorator() = contextmanager.getNode()
253+
)
254+
}
255+
243256
// =============================================================================
244257
// Callables
245258
// =============================================================================
@@ -1604,6 +1617,16 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
16041617
override ReturnKind getKind() { any() }
16051618
}
16061619

1620+
/** A data flow node that represents a value returned by a callable. */
1621+
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
1622+
YieldNodeInContextManagerFunction() {
1623+
hasContextmanagerDecorator(node.getScope()) and
1624+
node = any(Yield yield).getValue().getAFlowNode()
1625+
}
1626+
1627+
override ReturnKind getKind() { any() }
1628+
}
1629+
16071630
/** A data-flow node that represents the output of a call. */
16081631
abstract class OutNode extends Node {
16091632
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */

python/ql/test/experimental/dataflow/typetracking/test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,14 @@ def managed_resource():
223223
yield x # $ tracked
224224

225225
def test_context_manager():
226-
with managed_resource() as x: # $ MISSING: tracked
227-
print(x) # $ MISSING: tracked
226+
with managed_resource() as x: # $ tracked
227+
print(x) # $ tracked
228228

229229
@contextlib.contextmanager
230230
def managed_resource2():
231231
x = tracked # $ tracked
232232
yield x # $ tracked
233233

234234
def test_context_manager2():
235-
with managed_resource2() as x: # $ MISSING: tracked
236-
print(x) # $ MISSING: tracked
235+
with managed_resource2() as x: # $ tracked
236+
print(x) # $ tracked

0 commit comments

Comments
 (0)