diff --git a/tools/python/apixml2python.py b/tools/python/apixml2python.py index d6746ad08..2c32a24f6 100755 --- a/tools/python/apixml2python.py +++ b/tools/python/apixml2python.py @@ -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): diff --git a/tools/python/apixml2python/linphone.py b/tools/python/apixml2python/linphone.py index ce333a475..c1d37858d 100644 --- a/tools/python/apixml2python/linphone.py +++ b/tools/python/apixml2python/linphone.py @@ -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: diff --git a/tools/python/apixml2python/linphone_module.mustache b/tools/python/apixml2python/linphone_module.mustache index e31451cfa..13e0c1c6b 100644 --- a/tools/python/apixml2python/linphone_module.mustache +++ b/tools/python/apixml2python/linphone_module.mustache @@ -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; diff --git a/tools/python/doc/Makefile b/tools/python/doc/Makefile index b8c40c718..158616058 100644 --- a/tools/python/doc/Makefile +++ b/tools/python/doc/Makefile @@ -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." diff --git a/tools/python/doc/generate_enums.py b/tools/python/doc/generate_enums.py new file mode 100644 index 000000000..2f24a7a93 --- /dev/null +++ b/tools/python/doc/generate_enums.py @@ -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()) diff --git a/tools/python/doc/source/_static/pylinphone.js b/tools/python/doc/source/_static/pylinphone.js new file mode 100644 index 000000000..b0b7617a0 --- /dev/null +++ b/tools/python/doc/source/_static/pylinphone.js @@ -0,0 +1,52 @@ +$(function (){ +var createList = function(selector){ + + var ul = $('
'+e[0]+'
') + .append(l); + } + customIndex.append(ul); +}); + +}); \ No newline at end of file diff --git a/tools/python/doc/source/api_reference.rst b/tools/python/doc/source/api_reference.rst new file mode 100644 index 000000000..d08c68d77 --- /dev/null +++ b/tools/python/doc/source/api_reference.rst @@ -0,0 +1,24 @@ +API reference +============= + +Enums +----- + +.. include:: enums.rst + +Classes +------- + +.. container:: custom-index + + .. raw:: html + + + +.. currentmodule:: linphone + +.. automodule:: linphone + :members: + :undoc-members: + +.. autoattribute:: linphone.__version__ diff --git a/tools/python/doc/source/conf.py b/tools/python/doc/source/conf.py index 125e5c6d9..8dfeec972 100644 --- a/tools/python/doc/source/conf.py +++ b/tools/python/doc/source/conf.py @@ -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. diff --git a/tools/python/doc/source/getting_started.rst b/tools/python/doc/source/getting_started.rst new file mode 100644 index 000000000..f0b830c59 --- /dev/null +++ b/tools/python/doc/source/getting_started.rst @@ -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. diff --git a/tools/python/doc/source/index.rst b/tools/python/doc/source/index.rst index f1c615162..a77d768f0 100644 --- a/tools/python/doc/source/index.rst +++ b/tools/python/doc/source/index.rst @@ -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