mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-30 17:59:21 +00:00
Improve Python module documentation.
This commit is contained in:
parent
697a6d4a89
commit
42ddf0cf81
10 changed files with 224 additions and 77 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
28
tools/python/doc/generate_enums.py
Normal file
28
tools/python/doc/generate_enums.py
Normal 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())
|
||||
52
tools/python/doc/source/_static/pylinphone.js
Normal file
52
tools/python/doc/source/_static/pylinphone.js
Normal 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);
|
||||
});
|
||||
|
||||
});
|
||||
24
tools/python/doc/source/api_reference.rst
Normal file
24
tools/python/doc/source/api_reference.rst
Normal 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__
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
44
tools/python/doc/source/getting_started.rst
Normal file
44
tools/python/doc/source/getting_started.rst
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue