Improve Python module documentation.

This commit is contained in:
Ghislain MARY 2014-12-15 15:54:59 +01:00
parent 697a6d4a89
commit 42ddf0cf81
10 changed files with 224 additions and 77 deletions

View file

@ -71,13 +71,13 @@ blacklisted_functions = [
'lp_config_section_to_dict' # missing LinphoneDictionary
]
hand_written_functions = [
HandWrittenClassMethod('Buffer', 'new_from_data', 'linphone_buffer_new_from_data'),
HandWrittenProperty('Buffer', 'content', 'linphone_buffer_get_content', 'linphone_buffer_set_content'),
HandWrittenProperty('Content', 'buffer', 'linphone_content_get_buffer', 'linphone_content_set_buffer'),
HandWrittenProperty('Core', 'sound_devices', 'linphone_core_get_sound_devices', None),
HandWrittenProperty('Core', 'video_devices', 'linphone_core_get_video_devices', None),
HandWrittenClassMethod('Core', 'new', 'linphone_core_new'),
HandWrittenClassMethod('Core', 'new_with_config', 'linphone_core_new_with_config')
HandWrittenClassMethod('Buffer', 'new_from_data', 'linphone_buffer_new_from_data', "Create a new LinphoneBuffer object from existing data.\n\n:param data: The initial data to store in the LinphoneBuffer.\n:type data: ByteArray\n:returns: A new LinphoneBuffer object.\n:rtype: linphone.Buffer"),
HandWrittenProperty('Buffer', 'content', 'linphone_buffer_get_content', 'linphone_buffer_set_content', "[ByteArray] Set the content of the data buffer."),
HandWrittenProperty('Content', 'buffer', 'linphone_content_get_buffer', 'linphone_content_set_buffer', "[ByteArray] Set the content data buffer."),
HandWrittenProperty('Core', 'sound_devices', 'linphone_core_get_sound_devices', None, "[list of string] Get the available sound devices."),
HandWrittenProperty('Core', 'video_devices', 'linphone_core_get_video_devices', None, "[list of string] Get the available video capture devices."),
HandWrittenClassMethod('Core', 'new', 'linphone_core_new', "Instantiate a LinphoneCore object.\n\n:param vtable: The callbacks.\n:type vtable: dictionary\n:param configPath: A path to a config file. If it does not exists it will be created. The config file is used to store all settings, call logs, friends, proxies... so that all these settings become persistent over the life of the LinphoneCore object. It is allowed to set to None. In that case LinphoneCore will not store any settings.\n:type configPath: string\n:param factoryConfigPath: A path to a read-only config file that can be used to store hard-coded preference such as proxy settings or internal preferences. The settings in this factory file always override the one in the normal config file. It is OPTIONAL, use None if unneeded.\n:type factoryConfigPath: string\n:rtype: linphone.Core"),
HandWrittenClassMethod('Core', 'new_with_config', 'linphone_core_new_with_config', "Instantiate a LinphoneCore object from a LpConfig.\n\n:param vtable: The callbacks.\n:type vtable: dictionary\n:param config: A LpConfig object holding the configuration of the LinphoneCore to be instantiated.\n:rtype: linphone.Core")
]
def generate(apixmlfile, outputfile):

View file

@ -15,6 +15,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import re
from sets import Set
import sys
@ -25,6 +26,18 @@ def strip_leading_linphone(s):
else:
return s
def remove_useless_enum_prefix(senum, svalue):
lenum = re.findall('[A-Z][^A-Z]*', senum)
lvalue = re.findall('[A-Z][^A-Z]*', svalue)
if len(lenum) == 0 or len(lvalue) == 0:
return svalue
if lenum[0] == lvalue[0]:
i = 0
while i < len(lenum) and lenum[i] == lvalue[i]:
i += 1
return ''.join(lvalue[i:])
return svalue
def is_callback(s):
return s.startswith('Linphone') and s.endswith('Cb')
@ -42,27 +55,28 @@ def compute_event_name(s, className):
class HandWrittenCode:
def __init__(self, _class, name, func_list):
def __init__(self, _class, name, func_list, doc = ''):
self._class = _class
self.name = name
self.func_list = func_list
self.doc = doc
class HandWrittenInstanceMethod(HandWrittenCode):
def __init__(self, _class, name, cfunction):
HandWrittenCode.__init__(self, _class, name, [cfunction])
def __init__(self, _class, name, cfunction, doc = ''):
HandWrittenCode.__init__(self, _class, name, [cfunction], doc)
class HandWrittenClassMethod(HandWrittenCode):
def __init__(self, _class, name, cfunction):
HandWrittenCode.__init__(self, _class, name, [cfunction])
def __init__(self, _class, name, cfunction, doc = ''):
HandWrittenCode.__init__(self, _class, name, [cfunction], doc)
class HandWrittenProperty(HandWrittenCode):
def __init__(self, _class, name, getter_cfunction = None, setter_cfunction = None):
def __init__(self, _class, name, getter_cfunction = None, setter_cfunction = None, doc = ''):
func_list = []
if getter_cfunction is not None:
func_list.append(getter_cfunction)
if setter_cfunction is not None:
func_list.append(setter_cfunction)
HandWrittenCode.__init__(self, _class, name, func_list)
HandWrittenCode.__init__(self, _class, name, func_list, doc)
self.getter_cfunction = getter_cfunction
self.setter_cfunction = setter_cfunction
@ -947,18 +961,35 @@ class LinphoneModule(object):
e = {}
e['enum_name'] = strip_leading_linphone(xml_enum.get('name'))
e['enum_doc'] = self.__format_doc_content(xml_enum.find('briefdescription'), xml_enum.find('detaileddescription'))
e['enum_doc'] += "\n\nValues:\n"
e['enum_doc'] = self.__replace_doc_special_chars(e['enum_doc'])
e['enum_doc'] += """
.. csv-table::
:delim: |
:widths: 30, 70
:header: Value,Description
"""
e['enum_values'] = []
e['enum_deprecated_values'] = []
xml_enum_values = xml_enum.findall("./values/value")
for xml_enum_value in xml_enum_values:
if xml_enum_value.get('deprecated') == 'true':
continue
v = {}
v['enum_value_cname'] = xml_enum_value.get('name')
v['enum_value_name'] = strip_leading_linphone(v['enum_value_cname'])
valname = strip_leading_linphone(v['enum_value_cname'])
v['enum_value_name'] = remove_useless_enum_prefix(e['enum_name'], valname)
v['enum_value_doc'] = self.__format_doc(xml_enum_value.find('briefdescription'), xml_enum_value.find('detaileddescription'))
e['enum_doc'] += '\t' + v['enum_value_name'] + ': ' + v['enum_value_doc'] + '\n'
e['enum_doc'] += ' ' + v['enum_value_name'] + '|' + v['enum_value_doc'] + '\n'
e['enum_values'].append(v)
if v['enum_value_name'] != valname:
# TODO: To remove. Add deprecated value name.
v = {}
v['enum_value_cname'] = xml_enum_value.get('name')
v['enum_value_name'] = strip_leading_linphone(v['enum_value_cname'])
v['enum_value_doc'] = self.__format_doc(xml_enum_value.find('briefdescription'), xml_enum_value.find('detaileddescription'))
e['enum_deprecated_values'].append(v)
e['enum_doc'] = self.__replace_doc_special_chars(e['enum_doc'])
self.enums.append(e)
self.enum_names.append(e['enum_name'])
@ -1012,10 +1043,12 @@ class LinphoneModule(object):
if isinstance(hand_written_code, HandWrittenClassMethod):
m = {}
m['method_name'] = hand_written_code.name
m['method_doc'] = self.__replace_doc_special_chars(hand_written_code.doc)
c['class_type_hand_written_methods'].append(m)
elif isinstance(hand_written_code, HandWrittenInstanceMethod):
m = {}
m['method_name'] = hand_written_code.name
m['method_doc'] = self.__replace_doc_special_chars(hand_written_code.doc)
c['class_instance_hand_written_methods'].append(m)
elif isinstance(hand_written_code, HandWrittenProperty):
p = {}
@ -1028,6 +1061,7 @@ class LinphoneModule(object):
p['setter_reference'] = 'NULL'
else:
p['setter_reference'] = '(setter)pylinphone_' + c['class_name'] + '_set_' + p['property_name']
p['property_doc'] = self.__replace_doc_special_chars(hand_written_code.doc)
c['class_hand_written_properties'].append(p)
xml_type_methods = xml_class.findall("./classmethods/classmethod")
for xml_type_method in xml_type_methods:

View file

@ -148,14 +148,14 @@ static PyObject * pylinphone_{{class_name}}_instance_method_{{method_name}}(PyOb
static PyMethodDef pylinphone_{{class_name}}_methods[] = {
/* Class methods */
{{#class_type_hand_written_methods}}
{ "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "" },
{ "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "{{{method_doc}}}" },
{{/class_type_hand_written_methods}}
{{#class_type_methods}}
{ "{{method_name}}", pylinphone_{{class_name}}_class_method_{{method_name}}, METH_VARARGS | METH_CLASS, "{{{method_doc}}}" },
{{/class_type_methods}}
/* Instance methods */
{{#class_instance_hand_written_methods}}
{ "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "" },
{ "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "{{{method_doc}}}" },
{{/class_instance_hand_written_methods}}
{{#class_instance_methods}}
{ "{{method_name}}", pylinphone_{{class_name}}_instance_method_{{method_name}}, METH_VARARGS, "{{{method_doc}}}" },
@ -183,7 +183,7 @@ static PyMemberDef pylinphone_{{class_name}}_members[] = {
static PyGetSetDef pylinphone_{{class_name}}_getseters[] = {
{{#class_hand_written_properties}}
{ "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "" },
{ "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "{{{property_doc}}}" },
{{/class_hand_written_properties}}
{{#class_properties}}
{ "{{property_name}}", {{getter_reference}}, {{setter_reference}}, "{{{property_doc}}}" },
@ -308,12 +308,21 @@ PyMODINIT_FUNC initlinphone(void) {
{{#enum_values}}
if (PyModule_AddIntConstant(menum, "{{enum_value_name}}", {{enum_value_cname}}) < 0) return;
{{/enum_values}}
{{#enum_deprecated_values}}
if (PyModule_AddIntConstant(menum, "{{enum_value_name}}", {{enum_value_cname}}) < 0) return;
{{/enum_deprecated_values}}
{{/enums}}
menum = Py_InitModule3("PayloadTypeType", pylinphone_PayloadTypeType_ModuleMethods, "Type of linphone.PayloadType.");
menum = Py_InitModule3("PayloadTypeType", pylinphone_PayloadTypeType_ModuleMethods, "Type of linphone.PayloadType.\n\n.. csv-table::\n :delim: |\n :header: Value,Description\n\n AudioContinuous|\n AudioPacketized|\n Video|\n Text|\n Other|\n");
if (menum == NULL) return;
Py_INCREF(menum);
if (PyModule_AddObject(m, "PayloadTypeType", menum) < 0) return;
if (PyModule_AddIntConstant(menum, "AudioContinuous", PAYLOAD_AUDIO_CONTINUOUS) < 0) return;
if (PyModule_AddIntConstant(menum, "AudioPacketized", PAYLOAD_AUDIO_PACKETIZED) < 0) return;
if (PyModule_AddIntConstant(menum, "Video", PAYLOAD_VIDEO) < 0) return;
if (PyModule_AddIntConstant(menum, "Text", PAYLOAD_TEXT) < 0) return;
if (PyModule_AddIntConstant(menum, "Other", PAYLOAD_OTHER) < 0) return;
// TODO: To remove. Deprecated enum values.
if (PyModule_AddIntConstant(menum, "PAYLOAD_AUDIO_CONTINUOUS", PAYLOAD_AUDIO_CONTINUOUS) < 0) return;
if (PyModule_AddIntConstant(menum, "PAYLOAD_AUDIO_PACKETIZED", PAYLOAD_AUDIO_PACKETIZED) < 0) return;
if (PyModule_AddIntConstant(menum, "PAYLOAD_VIDEO", PAYLOAD_VIDEO) < 0) return;

View file

@ -48,8 +48,12 @@ help:
clean:
rm -rf $(BUILDDIR)/*
rm -f source/enums.rst
html:
source/enums.rst:
python generate_enums.py -o source/enums.rst
html: source/enums.rst
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

View file

@ -0,0 +1,28 @@
#!/usr/bin/python
import argparse
import types
import sys
def main(argv = None):
if argv is None:
argv = sys.argv
argparser = argparse.ArgumentParser(description="Generate enums documentation of the Linphone API.")
argparser.add_argument('-o', '--outputfile', metavar='outputfile', type=argparse.FileType('w'), help="Output .rst file describing the Linphone API enums.")
args = argparser.parse_args()
if args.outputfile == None:
args.outputfile = open('enums.rst', 'w')
module = __import__('linphone', globals(), locals())
for name in dir(module):
if name == 'testing' or name == 'linphone':
continue
if type(getattr(module, name)) == types.ModuleType:
args.outputfile.write('linphone.' + name + '\n')
args.outputfile.write('^' * len('linphone.' + name) + '\n\n')
args.outputfile.write(getattr(module, name).__doc__)
args.outputfile.write('\n')
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,52 @@
$(function (){
var createList = function(selector){
var ul = $('<ul>');
var selected = $(selector);
if (selected.length === 0){
return;
}
selected.clone().each(function (i,e){
var p = $(e).children('.descclassname');
var n = $(e).children('.descname');
var l = $(e).children('.headerlink');
var a = $('<a>');
a.attr('href',l.attr('href')).attr('title', 'Link to this definition');
a.append(p).append(n);
var entry = $('<li>').append(a);
ul.append(entry);
});
return ul;
}
var c = $('<div style="min-width: 300px;">');
var ul0 = c.clone().append($('.submodule-index'))
customIndex = $('.custom-index');
customIndex.empty();
customIndex.append(ul0);
var x = [];
x.push(['Classes','dl.class > dt']);
x.push(['Functions','dl.function > dt']);
x.push(['Variables','dl.data > dt']);
x.forEach(function (e){
var l = createList(e[1]);
if (l) {
var ul = c.clone()
.append('<p class="rubric">'+e[0]+'</p>')
.append(l);
}
customIndex.append(ul);
});
});

View file

@ -0,0 +1,24 @@
API reference
=============
Enums
-----
.. include:: enums.rst
Classes
-------
.. container:: custom-index
.. raw:: html
<script type="text/javascript" src='_static/pylinphone.js'></script>
.. currentmodule:: linphone
.. automodule:: linphone
:members:
:undoc-members:
.. autoattribute:: linphone.__version__

View file

@ -29,7 +29,7 @@ import os
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autodoc'
]
# Add any paths that contain templates here, relative to this directory.
@ -145,7 +145,9 @@ html_static_path = ['_static']
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
html_sidebars = {
'**': ['localtoc.html', 'relations.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.

View file

@ -0,0 +1,44 @@
Getting started
===============
Installing the Python module
----------------------------
You can install prebuilt packages of Linphone for Python. You will find the
releases at https://pypi.python.org/pypi/linphone. This includes only packages
for the Windows platform right now. The easiest way to install is to use pip,
eg.:
.. code-block:: none
> pip install linphone --pre
You can also find nightly-built packages for Windows, Mac OS X and Linux at
https://www.linphone.org/snapshots/linphone-python/.
Otherwise you can compile the Python module. To do so get the build system code
using the command:
.. code-block:: none
> git clone git://git.linphone.org/linphone-cmake-builder.git
Then follow the instructions in the *README.python* file.
Running some code
-----------------
Here is a sample source code using PyQt4 that enables you to register on a SIP
server in just a few lines of code. This is a very basic example, but it shows
how to create a linphone.Core object that is the main object of Linphone. From
there, you can use the API reference below to use more advanced feature and
perform some SIP calls, use text messaging and more...
.. literalinclude:: pyqt_linphone_example.py
In the Linphone Python module package you installed previously you will also
find some unit tests that you can run. These unit tests will be located in the
*site-packages/linphone/unittests/* directory of Python installation where you
installed the Linphone Python module package. To run these unit tests, follow
the instructions contained in the README.txt file of this *unittests/*
directory.

View file

@ -6,62 +6,12 @@
Linphone for Python documentation
=================================
Getting started
---------------
Installing the Python module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can install prebuilt packages of Linphone for Python. You will find the
releases at https://pypi.python.org/pypi/linphone. This includes only packages
for the Windows platform right now. The easiest way to install is to use pip,
eg.:
.. code-block:: none
> pip install linphone --pre
You can also find nightly-built packages for Windows, Mac OS X and Linux at
https://www.linphone.org/snapshots/linphone-python/.
Otherwise you can compile the Python module. To do so get the build system code
using the command:
.. code-block:: none
> git clone git://git.linphone.org/linphone-cmake-builder.git
Then follow the instructions in the *README.python* file.
Running some code
^^^^^^^^^^^^^^^^^
Here is a sample source code using PyQt4 that enables you to register on a SIP
server in just a few lines of code. This is a very basic example, but it shows
how to create a linphone.Core object that is the main object of Linphone. From
there, you can use the API reference below to use more advanced feature and
perform some SIP calls, use text messaging and more...
.. literalinclude:: pyqt_linphone_example.py
In the Linphone Python module package you installed previously you will also
find some unit tests that you can run. These unit tests will be located in the
*site-packages/linphone/unittests/* directory of Python installation where you
installed the Linphone Python module package. To run these unit tests, follow
the instructions contained in the README.txt file of this *unittests/*
directory.
API reference
-------------
.. toctree::
:maxdepth: 2
:numbered:
.. automodule:: linphone
:members:
:undoc-members:
.. autoattribute:: linphone.__version__
getting_started
api_reference
Indices and tables