docs: python bindings howto

* Added new advanced section with an example of using the Python
  bindings with CPython code compiled back to C code using Cython.
* Though it may seem a bit counter-intuitive to use the bindings just
  to go back to C via a different route, this is not actually stupid.
* Added examples/howto/advanced/cython/ directory.
* Added keycount.pyx, setup.py and the keycount.c file which the first
  two generated with Cython.  Not including the .so and .o files from
  the build.
* Exported the .texi version of the howto for the main docs.
This commit is contained in:
Ben McGinnes 2018-09-16 13:48:12 +10:00
parent fbec29fdac
commit 61c08f7435
5 changed files with 3254 additions and 0 deletions

View File

@ -27,6 +27,7 @@
* Working with keys:: * Working with keys::
* Basic Functions:: * Basic Functions::
* Creating keys and subkeys:: * Creating keys and subkeys::
* Advanced or Experimental Use Cases::
* Miscellaneous work-arounds:: * Miscellaneous work-arounds::
* Copyright and Licensing:: * Copyright and Licensing::
@ -120,6 +121,10 @@ User IDs
* Adding User IDs:: * Adding User IDs::
* Revokinging User IDs:: * Revokinging User IDs::
Advanced or Experimental Use Cases
* C plus Python plus SWIG plus Cython::
Miscellaneous work-arounds Miscellaneous work-arounds
* Group lines:: * Group lines::
@ -2061,6 +2066,120 @@ key = c.get_key(dmfpr, secret=True)
c.key_sign(key, uids=uid, expires_in=2764800) c.key_sign(key, uids=uid, expires_in=2764800)
@end example @end example
@node Advanced or Experimental Use Cases
@chapter Advanced or Experimental Use Cases
@menu
* C plus Python plus SWIG plus Cython::
@end menu
@node C plus Python plus SWIG plus Cython
@section C plus Python plus SWIG plus Cython
In spite of my near facetious commentary in @ref{CFFI is the Best™ and GPGME should use it instead of SWIG, , an earlier section}, it is
in fact quite possible to use the GPGME bindings with @uref{http://docs.cython.org/en/latest/index.html, Cython}. In many
cases the benefits may not be obvious since the most computationally
intensive work never leaves the level of the C code with which GPGME
itself is interacting with.
Nevertheless, there are some situations where the benefits are
demonstrable. One of the better and easier examples being the one of
the early examples in this HOWTO, the @ref{Counting keys, , key counting} code. Running that
example as an executable Python script, @samp{keycount.py} (available in
the @samp{examples/howto/} directory), will take a noticable amount of time
to run on most systems where the public keybox or keyring contains a
few thousand public keys.
Earlier in the evening I ran that script on my laptop, as I tend to do
periodically and timed it using @samp{time} utility, with the following
results:
@example
bash-4.4$ time keycount.py
Number of secret keys: 23
Number of public keys: 12112
real 11m52.945s
user 0m0.913s
sys 0m0.752s
bash-4.4$
@end example
Sometime after that I imported another key and followed it with a
little test of Cython. This test was kept fairly basic, essentially
lifting the material from the initial @uref{http://docs.cython.org/en/latest/src/tutorial/cython_tutorial.html#cython-hello-world, Cython Hello World tutorial} to
demonstrate compiling Python code to C. The first step was to take
the example key counting code quoted previously, essentially from the
importing of the @samp{gpg} module to the end of the script:
@example
import gpg
c = gpg.Context()
seckeys = c.keylist(pattern=None, secret=True)
pubkeys = c.keylist(pattern=None, secret=False)
seclist = list(seckeys)
secnum = len(seclist)
publist = list(pubkeys)
pubnum = len(publist)
print("""
Number of secret keys: @{0@}
Number of public keys: @{1@}
""".format(secnum, pubnum))
@end example
Save that into a file called @samp{keycount.pyx} and then create a
@samp{setup.py} file which contains this:
@example
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("keycount.pyx")
)
@end example
Compile it:
@example
bash-4.4$ python setup.py build_ext --inplace
bash-4.4$
@end example
Then run it in a similar manner to @samp{keycount.py}:
@example
bash-4.4$ time python3.7 -c "import keycount"
Number of secret keys: 23
Number of public keys: 12113
real 6m47.905s
user 0m0.785s
sys 0m0.331s
bash-4.4$
@end example
Cython turned @samp{keycount.pyx} into an 81KB @samp{keycount.o} file in the
@samp{build/} directory, a 24KB @samp{keycount.cpython-37m-darwin.so} file to be
imported into Python 3.7 and a 113KB @samp{keycount.c} generated C source
code file of nearly three thousand lines. Quite a bit bigger than the
314 bytes of the @samp{keycount.pyx} file or the full 1,452 bytes of the
full executable @samp{keycount.py} example script.
On the other hand it ran in nearly half the time; taking 6 minutes and
47.905 seconds to run. As opposed to the 11 minutes and 52.945 seconds
which the CPython script alone took.
@node Miscellaneous work-arounds @node Miscellaneous work-arounds
@chapter Miscellaneous work-arounds @chapter Miscellaneous work-arounds

View File

@ -1970,6 +1970,122 @@ c.key_sign(key, uids=uid, expires_in=2764800)
#+END_SRC #+END_SRC
* Advanced or Experimental Use Cases
:PROPERTIES:
:CUSTOM_ID: advanced-use
:END:
** C plus Python plus SWIG plus Cython
:PROPERTIES:
:CUSTOM_ID: cython
:END:
In spite of my near facetious commentary in [[#snafu-cffi][an earlier section]], it is
in fact quite possible to use the GPGME bindings with [[http://docs.cython.org/en/latest/index.html][Cython]]. In many
cases the benefits may not be obvious since the most computationally
intensive work never leaves the level of the C code with which GPGME
itself is interacting with.
Nevertheless, there are some situations where the benefits are
demonstrable. One of the better and easier examples being the one of
the early examples in this HOWTO, the [[#howto-keys-counting][key counting]] code. Running that
example as an executable Python script, =keycount.py= (available in
the =examples/howto/= directory), will take a noticable amount of time
to run on most systems where the public keybox or keyring contains a
few thousand public keys.
Earlier in the evening I ran that script on my laptop, as I tend to do
periodically and timed it using =time= utility, with the following
results:
#+BEGIN_SRC shell
bash-4.4$ time keycount.py
Number of secret keys: 23
Number of public keys: 12112
real 11m52.945s
user 0m0.913s
sys 0m0.752s
bash-4.4$
#+END_SRC
Sometime after that I imported another key and followed it with a
little test of Cython. This test was kept fairly basic, essentially
lifting the material from the initial [[http://docs.cython.org/en/latest/src/tutorial/cython_tutorial.html#cython-hello-world][Cython Hello World tutorial]] to
demonstrate compiling Python code to C. The first step was to take
the example key counting code quoted previously, essentially from the
importing of the =gpg= module to the end of the script:
#+BEGIN_SRC python -i
import gpg
c = gpg.Context()
seckeys = c.keylist(pattern=None, secret=True)
pubkeys = c.keylist(pattern=None, secret=False)
seclist = list(seckeys)
secnum = len(seclist)
publist = list(pubkeys)
pubnum = len(publist)
print("""
Number of secret keys: {0}
Number of public keys: {1}
""".format(secnum, pubnum))
#+END_SRC
Save that into a file called =keycount.pyx= and then create a
=setup.py= file which contains this:
#+BEGIN_SRC python -i
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("keycount.pyx")
)
#+END_SRC
Compile it:
#+BEGIN_SRC shell
bash-4.4$ python setup.py build_ext --inplace
bash-4.4$
#+END_SRC
Then run it in a similar manner to =keycount.py=:
#+BEGIN_SRC shell
bash-4.4$ time python3.7 -c "import keycount"
Number of secret keys: 23
Number of public keys: 12113
real 6m47.905s
user 0m0.785s
sys 0m0.331s
bash-4.4$
#+END_SRC
Cython turned =keycount.pyx= into an 81KB =keycount.o= file in the
=build/= directory, a 24KB =keycount.cpython-37m-darwin.so= file to be
imported into Python 3.7 and a 113KB =keycount.c= generated C source
code file of nearly three thousand lines. Quite a bit bigger than the
314 bytes of the =keycount.pyx= file or the full 1,452 bytes of the
full executable =keycount.py= example script.
On the other hand it ran in nearly half the time; taking 6 minutes and
47.905 seconds to run. As opposed to the 11 minutes and 52.945 seconds
which the CPython script alone took.
* Miscellaneous work-arounds * Miscellaneous work-arounds
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: cheats-and-hacks :CUSTOM_ID: cheats-and-hacks

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
import gpg
c = gpg.Context()
seckeys = c.keylist(pattern=None, secret=True)
pubkeys = c.keylist(pattern=None, secret=False)
seclist = list(seckeys)
secnum = len(seclist)
publist = list(pubkeys)
pubnum = len(publist)
print("""
Number of secret keys: {0}
Number of public keys: {1}
""".format(secnum, pubnum))

View File

@ -0,0 +1,6 @@
from distutils.core import setup
from Cython.Build import cythonize
setup(
ext_modules = cythonize("keycount.pyx")
)