Skip to content

Commit f9df73a

Browse files
committed
Some source re-org. Documentation additions.
1 parent 7abf89b commit f9df73a

File tree

14 files changed

+194
-98
lines changed

14 files changed

+194
-98
lines changed

.gitignore

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
PythonExtensionPatterns.bbprojectd/
2-
build/
2+
33
*.so
4+
build/
45
doc/sphinx/build/
6+
57
__pycache__/
8+
69
.DS_Store
710

11+
.idea
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>SchemeUserState</key>
6+
<dict>
7+
<key>PythonExtensionPatterns.xcscheme_^#shared#^_</key>
8+
<dict>
9+
<key>orderHint</key>
10+
<integer>0</integer>
11+
</dict>
12+
</dict>
13+
</dict>
14+
</plist>
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,79 @@
1-
.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
2-
.\"See Also:
3-
.\"man mdoc.samples for a complete listing of options
4-
.\"man mdoc for the short list of editing options
5-
.\"/usr/share/misc/mdoc.template
6-
.Dd 07/05/2014 \" DATE
7-
.Dt PythonExtensionPatterns 1 \" Program name and manual section number
8-
.Os Darwin
9-
.Sh NAME \" Section Header - required - don't modify
10-
.Nm PythonExtensionPatterns,
11-
.\" The following lines are read in generating the apropos(man -k) database. Use only key
12-
.\" words here as the database is built based on the words here and in the .ND line.
13-
.Nm Other_name_for_same_program(),
14-
.Nm Yet another name for the same program.
15-
.\" Use .Nm macro to designate other names for the documented program.
16-
.Nd This line parsed for whatis database.
17-
.Sh SYNOPSIS \" Section Header - required - don't modify
18-
.Nm
19-
.Op Fl abcd \" [-abcd]
20-
.Op Fl a Ar path \" [-a path]
21-
.Op Ar file \" [file]
22-
.Op Ar \" [file ...]
23-
.Ar arg0 \" Underlined argument - use .Ar anywhere to underline
24-
arg2 ... \" Arguments
25-
.Sh DESCRIPTION \" Section Header - required - don't modify
26-
Use the .Nm macro to refer to your program throughout the man page like such:
27-
.Nm
28-
Underlining is accomplished with the .Ar macro like this:
29-
.Ar underlined text .
30-
.Pp \" Inserts a space
31-
A list of items with descriptions:
32-
.Bl -tag -width -indent \" Begins a tagged list
33-
.It item a \" Each item preceded by .It macro
34-
Description of item a
35-
.It item b
36-
Description of item b
37-
.El \" Ends the list
38-
.Pp
39-
A list of flags and their descriptions:
40-
.Bl -tag -width -indent \" Differs from above in tag removed
41-
.It Fl a \"-a flag as a list item
42-
Description of -a flag
43-
.It Fl b
44-
Description of -b flag
45-
.El \" Ends the list
46-
.Pp
47-
.\" .Sh ENVIRONMENT \" May not be needed
48-
.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
49-
.\" .It Ev ENV_VAR_1
50-
.\" Description of ENV_VAR_1
51-
.\" .It Ev ENV_VAR_2
52-
.\" Description of ENV_VAR_2
53-
.\" .El
54-
.Sh FILES \" File used or created by the topic of the man page
55-
.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
56-
.It Pa /usr/share/file_name
57-
FILE_1 description
58-
.It Pa /Users/joeuser/Library/really_long_file_name
59-
FILE_2 description
60-
.El \" Ends the list
61-
.\" .Sh DIAGNOSTICS \" May not be needed
62-
.\" .Bl -diag
63-
.\" .It Diagnostic Tag
64-
.\" Diagnostic informtion here.
65-
.\" .It Diagnostic Tag
66-
.\" Diagnostic informtion here.
67-
.\" .El
68-
.Sh SEE ALSO
69-
.\" List links in ascending order by section, alphabetically within a section.
70-
.\" Please do not reference files that do not exist without filing a bug report
71-
.Xr a 1 ,
72-
.Xr b 1 ,
73-
.Xr c 1 ,
74-
.Xr a 2 ,
75-
.Xr b 2 ,
76-
.Xr a 3 ,
77-
.Xr b 3
78-
.\" .Sh BUGS \" Document known, unremedied bugs
1+
.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
2+
.\"See Also:
3+
.\"man mdoc.samples for a complete listing of options
4+
.\"man mdoc for the short list of editing options
5+
.\"/usr/share/misc/mdoc.template
6+
.Dd 07/05/2014 \" DATE
7+
.Dt PythonExtensionPatterns 1 \" Program name and manual section number
8+
.Os Darwin
9+
.Sh NAME \" Section Header - required - don't modify
10+
.Nm PythonExtensionPatterns,
11+
.\" The following lines are read in generating the apropos(man -k) database. Use only key
12+
.\" words here as the database is built based on the words here and in the .ND line.
13+
.Nm Other_name_for_same_program(),
14+
.Nm Yet another name for the same program.
15+
.\" Use .Nm macro to designate other names for the documented program.
16+
.Nd This line parsed for whatis database.
17+
.Sh SYNOPSIS \" Section Header - required - don't modify
18+
.Nm
19+
.Op Fl abcd \" [-abcd]
20+
.Op Fl a Ar path \" [-a path]
21+
.Op Ar file \" [file]
22+
.Op Ar \" [file ...]
23+
.Ar arg0 \" Underlined argument - use .Ar anywhere to underline
24+
arg2 ... \" Arguments
25+
.Sh DESCRIPTION \" Section Header - required - don't modify
26+
Use the .Nm macro to refer to your program throughout the man page like such:
27+
.Nm
28+
Underlining is accomplished with the .Ar macro like this:
29+
.Ar underlined text .
30+
.Pp \" Inserts a space
31+
A list of items with descriptions:
32+
.Bl -tag -width -indent \" Begins a tagged list
33+
.It item a \" Each item preceded by .It macro
34+
Description of item a
35+
.It item b
36+
Description of item b
37+
.El \" Ends the list
38+
.Pp
39+
A list of flags and their descriptions:
40+
.Bl -tag -width -indent \" Differs from above in tag removed
41+
.It Fl a \"-a flag as a list item
42+
Description of -a flag
43+
.It Fl b
44+
Description of -b flag
45+
.El \" Ends the list
46+
.Pp
47+
.\" .Sh ENVIRONMENT \" May not be needed
48+
.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
49+
.\" .It Ev ENV_VAR_1
50+
.\" Description of ENV_VAR_1
51+
.\" .It Ev ENV_VAR_2
52+
.\" Description of ENV_VAR_2
53+
.\" .El
54+
.Sh FILES \" File used or created by the topic of the man page
55+
.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
56+
.It Pa /usr/share/file_name
57+
FILE_1 description
58+
.It Pa /Users/joeuser/Library/really_long_file_name
59+
FILE_2 description
60+
.El \" Ends the list
61+
.\" .Sh DIAGNOSTICS \" May not be needed
62+
.\" .Bl -diag
63+
.\" .It Diagnostic Tag
64+
.\" Diagnostic informtion here.
65+
.\" .It Diagnostic Tag
66+
.\" Diagnostic informtion here.
67+
.\" .El
68+
.Sh SEE ALSO
69+
.\" List links in ascending order by section, alphabetically within a section.
70+
.\" Please do not reference files that do not exist without filing a bug report
71+
.Xr a 1 ,
72+
.Xr b 1 ,
73+
.Xr c 1 ,
74+
.Xr a 2 ,
75+
.Xr b 2 ,
76+
.Xr a 3 ,
77+
.Xr b 3
78+
.\" .Sh BUGS \" Document known, unremedied bugs
7979
.\" .Sh HISTORY \" Document history if command behaves in a unique manner

doc/sphinx/source/cpp_and_numpy.rst

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ If you are linking to the system Python this may not have numpy installed, here
221221
.. code-block:: bash
222222
223223
python -m venv <PATH_TO_VIRTUAL_ENVIRONMENT>
224+
source <PATH_TO_VIRTUAL_ENVIRONMENT>/bin/activate
224225
pip install numpy
225226
226227
Then in your C++ entry point add this function that manipulates ``sys.path``:

doc/sphinx/source/debugging/leak_newrefs_vg.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@ We can try our leaky code:
215215
216216
There is a big jump in ``tp_maxalloc`` for ints that is worth investigating.
217217

218-
When the Python process finishes you get a dump of this list as the interpreter is broken down::
218+
When the Python process finishes you get a dump of this list as the interpreter is broken down:
219219

220220
.. code-block:: console
221-
221+
222222
memoryview alloc'd: 210, freed: 210, max in use: 1
223223
managedbuffer alloc'd: 210, freed: 210, max in use: 1
224224
PrettyPrinter alloc'd: 2, freed: 2, max in use: 1

doc/sphinx/source/refcount.rst

+58-2
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,65 @@ The ``pLast = NULL;`` line is not necessary but is good coding style as it will
368368

369369
An important takeaway here is that incrementing and decrementing reference counts is a cheap operation but the consequences of getting it wrong can be expensive. A precautionary approach in your code might be to *always* increment borrowed references when they are instantiated and then *always* decrement them before they go out of scope. That way you incur two cheap operations but eliminate a vastly more expensive one.
370370

371-
^^^^^^^^^^^^^^^^^^
371+
-----------------------
372+
An Example Leak Problem
373+
-----------------------
374+
375+
Here is an example that exhibits a leak. The object is to add the integers 400 to 404 to the end of a list. You might want to study it to see if you can spot the problem:
376+
377+
.. code-block:: c
378+
379+
static PyObject *
380+
list_append_one_to_four(PyObject *list) {
381+
for (int i = 400; i < 405; ++i) {
382+
PyList_Append(list, PyLong_FromLong(i));
383+
}
384+
Py_RETURN_NONE;
385+
}
386+
387+
The problem is that ``PyLong_FromLong`` creates ``PyObject`` (an int) with a reference count of 1 **but** ``PyList_Append`` increments the reference count of the object passed to it by 1 to 2. This means when the list is destroyed the list element reference counts drop by one (to 1) but *no lower* as nothing else references them. Therefore they never get deallocated so there is a memory leak.
388+
389+
390+
The append operation *must* behave this way, consider this Python code
391+
392+
.. code-block:: python
393+
394+
l = []
395+
a = 400
396+
# The integer object '400' has a reference count of 1 as only
397+
# one symbol references it: ``a``.
398+
l.append(a)
399+
# The integer object '400' must now have a reference count of
400+
# 2 as two symbols reference it: ``a`` and ``l``,
401+
# specifically ``l[-1]``.
402+
403+
The fix for this code is to do this, error checking omitted:
404+
405+
.. code-block:: c
406+
407+
static PyObject *
408+
list_append_one_to_four(PyObject *list) {
409+
PyObject *temporary_item = NULL;
410+
411+
for (int i = 400; i < 405; ++i) {
412+
/* Create the object to append to the list. */
413+
temporary_item = PyLong_FromLong(i);
414+
/* Append it. This will increment the reference count. */
415+
PyList_Append(list, temporary_item);
416+
/* Decrement our reference to it leaving the list having the only reference. */
417+
Py_DECREF(temporary_item);
418+
/* Implementation detail really. */
419+
assert(temporary_item->ob_refcnt == 1);
420+
/* Good practice... */
421+
temporary_item = NULL;
422+
}
423+
Py_RETURN_NONE;
424+
}
425+
426+
427+
-----------------------
372428
Summary
373-
^^^^^^^^^^^^^^^^^^
429+
-----------------------
374430

375431
The contracts you enter into with these three reference types are:
376432

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ imagesize==1.2.0
88
Jinja2==2.11.2
99
MarkupSafe==1.1.1
1010
packaging==20.4
11+
psutil==5.7.2
1112
Pygments==2.7.1
1213
pyparsing==2.4.7
1314
pytz==2020.1

src/setup.py renamed to setup.py

+34-15
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,44 @@
77
"""
88
import os
99

10-
DEBUG = True
10+
from distutils.core import setup, Extension
11+
import os
12+
import sysconfig
13+
14+
DEBUG = False
15+
# Generally I write code so that if DEBUG is defined as 0 then all optimisations
16+
# are off and asserts are enabled. Typically run times of these builds are x2 to x10
17+
# release builds.
18+
# If DEBUG > 0 then extra code paths are introduced such as checking the integrity of
19+
# internal data structures. In this case the performance is by no means comparable
20+
# with release builds.
21+
DEBUG_LEVEL = 0
22+
23+
# Python stlib requirement:
24+
LANGUAGE_STANDARD = "c99"
25+
# Our level of C++
26+
#LANGUAGE_STANDARD = "c++11"
1127

12-
extra_compile_args=["-std=c99", ]
28+
# Common flags for both release and debug builds.
29+
extra_compile_args = sysconfig.get_config_var('CFLAGS').split()
30+
extra_compile_args += ["-std=%s" % LANGUAGE_STANDARD, "-Wall", "-Wextra"]
1331
if DEBUG:
14-
extra_compile_args += ["-g3", "-O0", "-DDEBUG=1",]
32+
extra_compile_args += ["-g3", "-O0", "-DDEBUG=%s" % DEBUG_LEVEL, "-UNDEBUG"]
1533
else:
16-
extra_compile_args += ["-DNDEBUG", "-Os"]
34+
extra_compile_args += ["-DNDEBUG", "-O3"]
1735

36+
PACKAGE_NAME = 'cPyExtPatt'
1837

1938
from distutils.core import setup, Extension
2039
setup(
21-
name = 'cPyExtPatt',
40+
name = PACKAGE_NAME,
2241
version = '0.1.0',
2342
author = 'Paul Ross',
24-
author_email = 'cpipdev@gmail.com',
43+
author_email = 'apaulross@gmail.com',
2544
maintainer = 'Paul Ross',
26-
maintainer_email = 'cpipdev@gmail.com',
27-
description = 'Python Extension Patterns.',
28-
long_description = """Examples of good and bad practice with Python Extensions.""",
45+
maintainer_email = 'apaulross@gmail.com',
46+
description = 'Python C Extension Patterns.',
47+
long_description = """Examples of good and bad practice with Python C Extensions.""",
2948
platforms = ['Mac OSX', 'POSIX',],
3049
classifiers = [
3150
'Development Status :: 3 - Alpha',
@@ -38,29 +57,29 @@
3857
'Programming Language :: Python',
3958
'Topic :: Programming',
4059
],
41-
license = 'GNU General Public License v2 (GPLv2)',
60+
licence = 'GNU General Public License v2 (GPLv2)',
4261
ext_modules=[
43-
Extension("cExceptions", sources=['cExceptions.c',],
62+
Extension(f"{PACKAGE_NAME}.cExceptions", sources=['src/cpy/cExceptions.c',],
4463
include_dirs = ['/usr/local/include',], # os.path.join(os.getcwd(), 'include'),],
4564
library_dirs = [os.getcwd(),], # path to .a or .so file(s)
4665
extra_compile_args=extra_compile_args,
4766
),
48-
Extension("cModuleGlobals", sources=['cModuleGlobals.c',],
67+
Extension(f"{PACKAGE_NAME}.cModuleGlobals", sources=['src/cpy/cModuleGlobals.c',],
4968
include_dirs = ['/usr/local/include',], # os.path.join(os.getcwd(), 'include'),],
5069
library_dirs = [os.getcwd(),], # path to .a or .so file(s)
5170
extra_compile_args=extra_compile_args,
5271
),
53-
Extension("cObj", sources=['cObjmodule.c',],
72+
Extension(f"{PACKAGE_NAME}.cObj", sources=['src/cpy/cObjmodule.c',],
5473
include_dirs = ['/usr/local/include',], # os.path.join(os.getcwd(), 'include'),],
5574
library_dirs = [os.getcwd(),], # path to .a or .so file(s)
5675
extra_compile_args=extra_compile_args,
5776
),
58-
Extension("cParseArgs", sources=['cParseArgs.c',],
77+
Extension(f"{PACKAGE_NAME}.cParseArgs", sources=['src/cpy/cParseArgs.c',],
5978
include_dirs = ['/usr/local/include',], # os.path.join(os.getcwd(), 'include'),],
6079
library_dirs = [os.getcwd(),], # path to .a or .so file(s)
6180
extra_compile_args=extra_compile_args,
6281
),
63-
Extension("cPyRefs", sources=['cPyRefs.c',],
82+
Extension(f"{PACKAGE_NAME}.cPyRefs", sources=['src/cpy/cPyRefs.c',],
6483
include_dirs = ['/usr/local/include',], # os.path.join(os.getcwd(), 'include'),],
6584
library_dirs = [os.getcwd(),], # path to .a or .so file(s)
6685
#libraries = ['jpeg',],
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)