Skip to content

Commit 45f5f5d

Browse files
committed
test: add context manager tests for __enter__ behavior
fix: update 'with' macro to bind return value of __enter__ correctly
1 parent 7f57221 commit 45f5f5d

File tree

3 files changed

+47
-22
lines changed

3 files changed

+47
-22
lines changed

src/libpython_clj2/python/with.clj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,16 @@
4949
[bind-vec & body]
5050
(when-not (= 2 (count bind-vec))
5151
(throw (Exception. "Bind vector must have 2 items")))
52-
(let [varname (first bind-vec)]
52+
(let [varname (first bind-vec)
53+
mgr (gensym "mgr")]
5354
`(py-ffi/with-gil
54-
(let [~@bind-vec]
55+
(let [~mgr ~(second bind-vec)]
5556
(with-bindings
5657
{#'py-ffi/*python-error-handler* python-pyerr-fetch-error-handler}
57-
(py-fn/call-attr ~varname "__enter__" nil)
58-
(try
59-
(let [retval# (do ~@body)]
60-
(py-fn/call-attr ~varname "__exit__" [nil nil nil])
61-
retval#)
62-
(catch Throwable e#
63-
(with-exit-error-handler ~varname e#))))))))
58+
(let [~varname (py-fn/call-attr ~mgr "__enter__" nil)]
59+
(try
60+
(let [retval# (do ~@body)]
61+
(py-fn/call-attr ~mgr "__exit__" [nil nil nil])
62+
retval#)
63+
(catch Throwable e#
64+
(with-exit-error-handler ~mgr e#)))))))))

test/libpython_clj2/python_test.clj

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@
192192
(is (= ["enter" "exit: None"]
193193
(py/->jvm fn-list))))))
194194

195+
(deftest with-enter-returns-different-object
196+
(testing "py/with should bind the return value of __enter__, not the context manager"
197+
(let [testcode (py/import-module "testcode")]
198+
(py/with [f (py/call-attr testcode "FileWrapper" "test content")]
199+
;; f should be the StringIO object returned by __enter__, not FileWrapper
200+
(is (= "test content" (py/call-attr f "read")))))))
201+
195202
(deftest arrow-as-fns-with-nil
196203
(is (= nil (py/->jvm nil)))
197204
(is (= nil (py/as-jvm nil))))
@@ -449,6 +456,4 @@ class Foo:
449456

450457
(let [data (doto (pd/DataFrame {:index [1 2] :value [2 3] :variable [1 1]})
451458
(py. melt :id_vars "index"))]
452-
((py.- px line) :data_frame data :x "index" :y "value" :color "variable"))
453-
454-
)
459+
((py.- px line) :data_frame data :x "index" :y "value" :color "variable")))

testcode/__init__.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,38 @@ class WithObjClass:
22
def __init__(self, suppress, fn_list):
33
self.suppress = suppress
44
self.fn_list = fn_list
5+
56
def __enter__(self):
67
self.fn_list.append("enter")
8+
return self # Return self so methods can be called on the bound variable
9+
710
def doit_noerr(self):
811
return 1
12+
913
def doit_err(self):
1014
raise Exception("Spam", "Eggs")
15+
1116
def __exit__(self, ex_type, ex_val, ex_traceback):
1217
self.fn_list.append("exit: " + str(ex_val))
1318
return self.suppress
1419

1520

21+
class FileWrapper:
22+
"""Context manager where __enter__ returns a different object"""
23+
24+
def __init__(self, content):
25+
self.content = content
26+
27+
def __enter__(self):
28+
# Return a different object with the content
29+
import io
30+
31+
return io.StringIO(self.content)
32+
33+
def __exit__(self, *args):
34+
return False
35+
36+
1637
def for_iter(arg):
1738
retval = []
1839
for item in arg:
@@ -24,15 +45,13 @@ def calling_custom_clojure_fn(arg):
2445
return arg.clojure_fn()
2546

2647

27-
28-
def complex_fn(a, b, c: str=5, *args, d=10, **kwargs):
29-
return {"a" : a,
30-
"b" : b,
31-
"c" : c,
32-
"args" : args,
33-
"d": d,
34-
"kwargs": kwargs}
48+
def complex_fn(a, b, c: str = 5, *args, d=10, **kwargs):
49+
return {"a": a, "b": b, "c": c, "args": args, "d": d, "kwargs": kwargs}
3550

3651

37-
complex_fn_testcases = {"complex_fn(1, 2, c=10, d=10, e=10)":complex_fn(1, 2, c=10, d=10, e=10),
38-
"complex_fn(1, 2, 10, 11, 12, d=10, e=10)":complex_fn(1, 2, 10, 11, 12, d=10, e=10)}
52+
complex_fn_testcases = {
53+
"complex_fn(1, 2, c=10, d=10, e=10)": complex_fn(1, 2, c=10, d=10, e=10),
54+
"complex_fn(1, 2, 10, 11, 12, d=10, e=10)": complex_fn(
55+
1, 2, 10, 11, 12, d=10, e=10
56+
),
57+
}

0 commit comments

Comments
 (0)