Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 610a044

Browse files
committed
Review edits of context_managers.rst. Build all documentation with one cross-reference error.
1 parent cf3cbab commit 610a044

File tree

5 files changed

+248
-55
lines changed

5 files changed

+248
-55
lines changed

‎doc/sphinx/source/context_manager.rst

Lines changed: 229 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
.. toctree::
55
:maxdepth: 3
66

7+
.. _context manger: https://docs.python.org/3/glossary.html#term-context-manager>
8+
.. _\__enter__(): https://docs.python.org/3/library/stdtypes.html#contextmanager.__enter__
9+
.. _\__exit__(): https://docs.python.org/3/library/stdtypes.html#contextmanager.__exit__
10+
711
.. _chapter_context_manager:
812

913
.. index::
@@ -14,7 +18,6 @@ Context Managers
1418
***************************
1519

1620
This chapter describes how to write
17-
`context mangers <https://docs.python.org/3/glossary.html#term-context-manager>`_
1821
for your C objects.
1922

2023
.. index::
@@ -24,8 +27,7 @@ for your C objects.
2427
C Functions
2528
===========================
2629

27-
This is a summary of what is required for the C functions implementing a context
28-
manager.
30+
This is a summary of what is required for the C functions implementing a `context manger`_.
2931
The is no specific ``tp_...`` slot for the context manager functions ``__enter__`` and ``__exit__``, instead they are added
3032
to the object as named, looked up, Python methods.
3133

@@ -36,19 +38,6 @@ to the object as named, looked up, Python methods.
3638
``__enter__``
3739
--------------------------------------
3840

39-
Note that ``__enter__`` is declared with ``METH_NOARGS``.
40-
Link to the Python documentation for
41-
`__enter__ <https://docs.python.org/3/library/stdtypes.html#contextmanager.__enter__>`_.
42-
43-
.. code-block:: c
44-
45-
static PyMethodDef ContextManager_methods[] = {
46-
{"__enter__", (PyCFunction) ContextManager_enter, METH_NOARGS,
47-
PyDoc_STR("__enter__() -> ContextManager")},
48-
/* ... */
49-
{NULL, NULL, 0, NULL} /* sentinel */
50-
};
51-
5241
The C function must, at least, increment the reference count of ``self`` and
5342
return ``self``:
5443

@@ -68,20 +57,8 @@ return ``self``:
6857
``__exit__``
6958
--------------------------------------
7059

71-
The ``__exit__`` function is declared thus.
60+
The `__exit__()`_ function is declared thus.
7261
It takes three arguments so ``METH_VARARGS`` is used.
73-
Link to the Python documentation for
74-
`__exit__ <https://docs.python.org/3/library/stdtypes.html#contextmanager.__exit__>`_.
75-
76-
.. code-block:: c
77-
78-
static PyMethodDef ContextManager_methods[] = {
79-
{"__exit__", (PyCFunction) ContextManager_exit, METH_VARARGS,
80-
PyDoc_STR("__exit__(exc_type, exc_value, exc_tb) -> bool")},
81-
/* ... */
82-
{NULL, NULL, 0, NULL} /* sentinel */
83-
};
84-
8562
The three arguments are each ``None`` if no exception has been raised within
8663
the ``with`` block.
8764
If an exception *has* been raised within the ``with`` block then the
@@ -109,6 +86,24 @@ CPython interpreter:
10986
Py_RETURN_FALSE;
11087
}
11188
89+
--------------------------------------
90+
Method declarations
91+
--------------------------------------
92+
93+
Note that `__enter__()`_ is declared with ``METH_NOARGS`` and `__exit__()`_ is declared with ``METH_VARARGS``:
94+
95+
.. code-block:: c
96+
97+
static PyMethodDef ContextManager_methods[] = {
98+
/* ... */
99+
{"__enter__", (PyCFunction) ContextManager_enter, METH_NOARGS,
100+
PyDoc_STR("__enter__() -> ContextManager")},
101+
{"__exit__", (PyCFunction) ContextManager_exit, METH_VARARGS,
102+
PyDoc_STR("__exit__(exc_type, exc_value, exc_tb) -> bool")},
103+
/* ... */
104+
{NULL, NULL, 0, NULL} /* sentinel */
105+
};
106+
112107
=================================
113108
Understanding the Context Manager
114109
=================================
@@ -153,14 +148,14 @@ The sequence of reference count changes are as follows:
153148
(to 2) and then call ``__enter__`` that is implemented in our C function
154149
``ContextManager_enter``.
155150
#. Our ``ContextManager_enter`` function increments the reference count, so it is now 3.
156-
#. As the context manager exists the scope of the ``with`` statement the CPython interpreter
151+
#. As the context manager ends the ``with`` statement the CPython interpreter
157152
decrements the reference count *twice* to the value 1.
158153
The logic is:
159154

160155
#. Decrement the reference count once as we are exiting the ``with`` statement. The reference count is now 2.
161156
#. Did the ``with`` statement have a target? If not, as in this case, then decrement the reference count once more. The reference count is now 1.
162157

163-
#. The CPython interpreter then calls ``__exit__`` which is implemented in our function
158+
#. after the ``pass`` statement the CPython interpreter then calls ``__exit__`` which is implemented in our function
164159
``ContextManager_exit``.
165160
This does not change the reference count which remains at 1.
166161
#. As the context manager goes out of scope the CPython interpreter decrements the reference
@@ -211,9 +206,9 @@ The sequence of reference count changes are now as follows:
211206
#. As above, the reference count becomes 1.
212207
#. As above, the reference count becomes 2.
213208
#. As above, the reference count becomes 3.
214-
#. As the context manager exists the scope of the ``with`` statement the CPython interpreter
215-
decrements the reference count just *once* to the value 2 as there *is* a target.
216-
#. The CPython interpreter then calls ``__exit__`` which is implemented in our function
209+
#. As the context manager ends the ``with`` statement the CPython interpreter
210+
decrements the reference count just *once* to the value 2 as there *is* a target, called ``context`` in this case.
211+
#. After the ``pass`` statement the CPython interpreter then calls ``__exit__`` which is implemented in our function
217212
``ContextManager_exit``.
218213
This does not change the reference count which remains at 2.
219214
#. As the context manager goes out of scope the CPython interpreter decrements the reference
@@ -256,6 +251,10 @@ First the object declaration, allocation and de-allocation functions:
256251
PyObject_Del(self);
257252
}
258253
254+
-----------------------------------------
255+
``__enter__`` and ``__exit__`` Methods
256+
-----------------------------------------
257+
259258
The ``__enter__`` and ``__exit__`` methods:
260259

261260
.. code-block:: c
@@ -279,6 +278,11 @@ The ``__enter__`` and ``__exit__`` methods:
279278
{NULL, NULL, 0, NULL} /* sentinel */
280279
};
281280
281+
282+
-----------------------------------------
283+
Type Declaration
284+
-----------------------------------------
285+
282286
The type declaration:
283287

284288
.. code-block:: c
@@ -293,6 +297,9 @@ The type declaration:
293297
.tp_new = (newfunc) ContextManager_new,
294298
};
295299
300+
-----------------------------------------
301+
Module
302+
-----------------------------------------
296303

297304
Finally the module:
298305

@@ -326,11 +333,9 @@ Finally the module:
326333
return NULL;
327334
}
328335
329-
.. note::
330-
331-
The actual code in ``src/cpy/CtxMgr/cCtxMgr.c`` contains extra trace
332-
reporting that confirms the reference counts and (no) memory leakage.
333-
336+
-----------------------------------------
337+
Setup
338+
-----------------------------------------
334339

335340
This code is added to the ``setup.py`` file:
336341

@@ -351,3 +356,188 @@ And can be used thus:
351356
352357
with cCtxMgr.ContextManager():
353358
pass
359+
360+
-----------------------------------------
361+
Testing
362+
-----------------------------------------
363+
364+
The actual code in ``src/cpy/CtxMgr/cCtxMgr.c`` contains extra trace reporting that confirms the reference counts and
365+
(no) memory leakage.
366+
367+
This can be run with:
368+
369+
.. code-block:: bash
370+
371+
$ pytest tests/unit/test_c_ctxmgr.py -vs
372+
373+
This test:
374+
375+
.. code-block:: python
376+
377+
def test_very_simple():
378+
print()
379+
with cCtxMgr.ContextManager():
380+
pass
381+
382+
Gives this output:
383+
384+
.. code-block:: text
385+
386+
tests/unit/test_c_ctxmgr.py::test_very_simple
387+
ContextManager_new DONE REFCNT = 1
388+
ContextManager_enter STRT REFCNT = 2
389+
ContextManager_enter DONE REFCNT = 3
390+
ContextManager_exit STRT REFCNT = 1
391+
ContextManager_exit DONE REFCNT = 1
392+
ContextManager_dealloc STRT REFCNT = 0
393+
ContextManager_dealloc DONE REFCNT = 4546088432
394+
395+
This test:
396+
397+
.. code-block:: python
398+
399+
def test_simple():
400+
print()
401+
with cCtxMgr.ContextManager() as context:
402+
assert sys.getrefcount(context) == 3
403+
assert context.len_buffer_lifetime() == cCtxMgr.BUFFER_LENGTH
404+
assert context.len_buffer_context() == cCtxMgr.BUFFER_LENGTH
405+
assert sys.getrefcount(context) == 2
406+
assert context.len_buffer_lifetime() == cCtxMgr.BUFFER_LENGTH
407+
assert context.len_buffer_context() == 0
408+
del context
409+
410+
Gives this output:
411+
412+
.. code-block:: text
413+
414+
tests/unit/test_c_ctxmgr.py::test_simple
415+
ContextManager_new DONE REFCNT = 1
416+
ContextManager_enter STRT REFCNT = 2
417+
ContextManager_enter DONE REFCNT = 3
418+
ContextManager_exit STRT REFCNT = 2
419+
ContextManager_exit DONE REFCNT = 2
420+
ContextManager_dealloc STRT REFCNT = 0
421+
ContextManager_dealloc DONE REFCNT = 4546096048
422+
423+
424+
This test:
425+
426+
.. code-block:: python
427+
428+
def test_memory():
429+
proc = psutil.Process()
430+
print()
431+
print(f'RSS START: {proc.memory_info().rss:12,d}')
432+
for i in range(8):
433+
print(f'RSS START {i:5d}: {proc.memory_info().rss:12,d}')
434+
with cCtxMgr.ContextManager() as context:
435+
print(f'RSS START CTX: {proc.memory_info().rss:12,d}')
436+
# Does not work in the debugger due to introspection.
437+
# assert sys.getrefcount(context) == 3
438+
assert context.len_buffer_lifetime() == cCtxMgr.BUFFER_LENGTH
439+
assert context.len_buffer_context() == cCtxMgr.BUFFER_LENGTH
440+
print(f'RSS END CTX: {proc.memory_info().rss:12,d}')
441+
# Does not work in the debugger due to introspection.
442+
# assert sys.getrefcount(context) == 2
443+
assert context.len_buffer_lifetime() == cCtxMgr.BUFFER_LENGTH
444+
assert context.len_buffer_context() == 0
445+
del context
446+
print(f'RSS END {i:5d}: {proc.memory_info().rss:12,d}')
447+
print(f'RSS END: {proc.memory_info().rss:12,d}')
448+
449+
Gives this output:
450+
451+
.. code-block:: text
452+
453+
tests/unit/test_c_ctxmgr.py::test_memory
454+
RSS START: 300,032,000
455+
RSS START 0: 300,048,384
456+
ContextManager_new DONE REFCNT = 1
457+
ContextManager_enter STRT REFCNT = 2
458+
ContextManager_enter DONE REFCNT = 3
459+
RSS START CTX: 300,048,384
460+
RSS END CTX: 300,048,384
461+
ContextManager_exit STRT REFCNT = 2
462+
ContextManager_exit DONE REFCNT = 2
463+
ContextManager_dealloc STRT REFCNT = 0
464+
ContextManager_dealloc DONE REFCNT = 4546096048
465+
RSS END 0: 300,048,384
466+
RSS START 1: 300,048,384
467+
ContextManager_new DONE REFCNT = 1
468+
ContextManager_enter STRT REFCNT = 2
469+
ContextManager_enter DONE REFCNT = 3
470+
RSS START CTX: 300,048,384
471+
RSS END CTX: 300,048,384
472+
ContextManager_exit STRT REFCNT = 2
473+
ContextManager_exit DONE REFCNT = 2
474+
ContextManager_dealloc STRT REFCNT = 0
475+
ContextManager_dealloc DONE REFCNT = 4546096048
476+
RSS END 1: 300,048,384
477+
RSS START 2: 300,048,384
478+
ContextManager_new DONE REFCNT = 1
479+
ContextManager_enter STRT REFCNT = 2
480+
ContextManager_enter DONE REFCNT = 3
481+
RSS START CTX: 300,048,384
482+
RSS END CTX: 300,048,384
483+
ContextManager_exit STRT REFCNT = 2
484+
ContextManager_exit DONE REFCNT = 2
485+
ContextManager_dealloc STRT REFCNT = 0
486+
ContextManager_dealloc DONE REFCNT = 4546096048
487+
RSS END 2: 300,048,384
488+
RSS START 3: 300,048,384
489+
ContextManager_new DONE REFCNT = 1
490+
ContextManager_enter STRT REFCNT = 2
491+
ContextManager_enter DONE REFCNT = 3
492+
RSS START CTX: 300,048,384
493+
RSS END CTX: 300,048,384
494+
ContextManager_exit STRT REFCNT = 2
495+
ContextManager_exit DONE REFCNT = 2
496+
ContextManager_dealloc STRT REFCNT = 0
497+
ContextManager_dealloc DONE REFCNT = 4546096048
498+
RSS END 3: 300,048,384
499+
RSS START 4: 300,048,384
500+
ContextManager_new DONE REFCNT = 1
501+
ContextManager_enter STRT REFCNT = 2
502+
ContextManager_enter DONE REFCNT = 3
503+
RSS START CTX: 300,048,384
504+
RSS END CTX: 300,048,384
505+
ContextManager_exit STRT REFCNT = 2
506+
ContextManager_exit DONE REFCNT = 2
507+
ContextManager_dealloc STRT REFCNT = 0
508+
ContextManager_dealloc DONE REFCNT = 4546096048
509+
RSS END 4: 300,048,384
510+
RSS START 5: 300,048,384
511+
ContextManager_new DONE REFCNT = 1
512+
ContextManager_enter STRT REFCNT = 2
513+
ContextManager_enter DONE REFCNT = 3
514+
RSS START CTX: 300,048,384
515+
RSS END CTX: 300,048,384
516+
ContextManager_exit STRT REFCNT = 2
517+
ContextManager_exit DONE REFCNT = 2
518+
ContextManager_dealloc STRT REFCNT = 0
519+
ContextManager_dealloc DONE REFCNT = 4546096048
520+
RSS END 5: 300,048,384
521+
RSS START 6: 300,048,384
522+
ContextManager_new DONE REFCNT = 1
523+
ContextManager_enter STRT REFCNT = 2
524+
ContextManager_enter DONE REFCNT = 3
525+
RSS START CTX: 300,048,384
526+
RSS END CTX: 300,048,384
527+
ContextManager_exit STRT REFCNT = 2
528+
ContextManager_exit DONE REFCNT = 2
529+
ContextManager_dealloc STRT REFCNT = 0
530+
ContextManager_dealloc DONE REFCNT = 4546096048
531+
RSS END 6: 300,048,384
532+
RSS START 7: 300,048,384
533+
ContextManager_new DONE REFCNT = 1
534+
ContextManager_enter STRT REFCNT = 2
535+
ContextManager_enter DONE REFCNT = 3
536+
RSS START CTX: 300,048,384
537+
RSS END CTX: 300,048,384
538+
ContextManager_exit STRT REFCNT = 2
539+
ContextManager_exit DONE REFCNT = 2
540+
ContextManager_dealloc STRT REFCNT = 0
541+
ContextManager_dealloc DONE REFCNT = 4546096048
542+
RSS END 7: 300,048,384
543+
RSS END: 300,048,384

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /