From 979051c7896c5e216323464b6004c27119caa3b7 Mon Sep 17 00:00:00 2001 From: David Beitey Date: Mon, 26 Aug 2013 17:35:30 +1000 Subject: [PATCH 1/4] Initial implementation of Sphinx documentation --- .gitignore | 4 + README.md | 83 ---------------- README.rst | 104 +++++++++++++++++++ docs/Makefile | 177 +++++++++++++++++++++++++++++++++ docs/api.rst | 16 +++ docs/conf.py | 248 ++++++++++++++++++++++++++++++++++++++++++++++ docs/examples.rst | 37 +++++++ docs/index.rst | 30 ++++++ docs/make.bat | 242 ++++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 10 files changed, 859 insertions(+), 83 deletions(-) delete mode 100644 README.md create mode 100644 README.rst create mode 100644 docs/Makefile create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 docs/make.bat diff --git a/.gitignore b/.gitignore index 5d58718..b06378c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.egg-info dist build +docs/_build # Eclipse project info .project @@ -24,3 +25,6 @@ tools/init # Unit tests / coverage reports .coverage htmlcov + +# Working copy files +*.swp diff --git a/README.md b/README.md deleted file mode 100644 index 9f9e5ed..0000000 --- a/README.md +++ /dev/null @@ -1,83 +0,0 @@ -python-gsmmodem -=============== -*GSM modem module for Python* - -python-gsmmodem is a module that allows easy control of a GSM modem attached -to the system. It also includes a couple of useful commandline utilities for -interacting with a GSM modem. - -Its features include: - -- simple methods for sending SMS messages, checking signal level, etc -- easy-to-use API for starting and responding to USSD sessions and making voice calls -- handling incoming phone calls and received SMS messages via callback methods -- support for SMS PDU and text mode -- support for tracking SMS status reports -- wraps AT command errors into Python exceptions by default -- modular design; you easily issue your own AT commands to the modem (with error checking), - or read/write directly from/to the modem if you prefer -- comprehensive test suite - -Bundled utilities: - -- **GSMTerm**: an easy-to-use serial terminal for communicating with an attached GSM - modem. It features command completion, built-in help for many AT commands, - history, context-aware prompt, etc. -- **sendsms.py**: a simple command line script to send SMS messages -- **identify-modem.py**: simple utility to identify attached modem. Can also be used to - provide debug information used for development of python-gsmmodem. - -Requirements ------------- - -- Python 2.6 or later -- pyserial - - -How to install this package ---------------------------- - -There are two ways to install python-gsmmodem: - -#### Option 1: Automatic installation using pip #### - -> pip install python-gsmmodem - -[pip](http://www.pip-installer.org) will automatically download and install pyserial if needed. - -#### Option 2: Manual installation #### - -Download and extract python-gsmmodem. Next, do: - -> python setup.py install - -Note that python-gsmmodem relies on pyserial for serial communications: -http://pyserial.sourceforge.net - - -Testing the package -------------------- - -[![Build Status](https://travis-ci.org/faucamp/python-gsmmodem.png?branch=master)](https://travis-ci.org/faucamp/python-gsmmodem) -[![Coverage Status](https://coveralls.io/repos/faucamp/python-gsmmodem/badge.png?branch=master)](https://coveralls.io/r/faucamp/python-gsmmodem) - -To run all unit tests, do: - -> python setup.py test - -Unit test code coverage information may be generated by using [coverage](https://pypi.python.org/pypi/coverage/). -You can execute it directly from setup.py by doing: - -> python setup.py coverage - -This will run all unit tests and report on code coverage statistics. - - -License information -------------------- - -Copyright (C) 2013 Francois Aucamp -See AUTHORS for all authors and contact information. - -License: GNU Lesser General Public License, version 3 or later; see COPYING - included in this archive for details. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..9f214f7 --- /dev/null +++ b/README.rst @@ -0,0 +1,104 @@ +python-gsmmodem +=============== +*GSM modem module for Python* + +python-gsmmodem is a module that allows easy control of a GSM modem attached +to the system. It also includes a couple of useful commandline utilities for +interacting with a GSM modem. + +Its features include: + +- simple methods for sending SMS messages, checking signal level, etc +- easy-to-use API for starting and responding to USSD sessions and making voice + calls +- handling incoming phone calls and received SMS messages via callback methods +- support for SMS PDU and text mode +- support for tracking SMS status reports +- wraps AT command errors into Python exceptions by default +- modular design; you easily issue your own AT commands to the modem (with + error checking), or read/write directly from/to the modem if you prefer +- comprehensive test suite + +Bundled utilities: + +- **GSMTerm**: an easy-to-use serial terminal for communicating with an + attached GSM modem. It features command completion, built-in help for many AT + commands, history, context-aware prompt, etc. +- **sendsms.py**: a simple command line script to send SMS messages +- **identify-modem.py**: simple utility to identify attached modem. Can also be + used to provide debug information used for development of python-gsmmodem. + +Requirements +------------ + +- Python 2.6 or later +- pyserial + + +How to install this package +--------------------------- + +There are two ways to install ``python-gsmmodem``: + +Automatic installation +~~~~~~~~~~~~~~~~~~~~~~ + +:: + + pip install python-gsmmodem + +`pip `_ will automatically download and install +all dependencies, as required. You can also utilise ``easy_install`` in the +same manner as using ``pip`` above. + +If you are utilising ``python-gsmmodem`` as part of another project, +add it to your ``install_requires`` section of your ``setup.py`` file and +upon your project's installation, it will be pulled in automatically. + +Manual installation +~~~~~~~~~~~~~~~~~~~ + +Download and extract the ``python-gsmmodem`` archive from `PyPI +`_ for the current release +version, or clone from `GitHub `_. +Next, do this:: + + python setup.py install + +Note that ``python-gsmmodem`` relies on ``pyserial`` for serial communications: +http://pyserial.sourceforge.net + + +Testing the package +------------------- + +.. |Build Status| image:: https://travis-ci.org/faucamp/python-gsmmodem.png?branch=master +.. _Build Status: https://travis-ci.org/faucamp/python-gsmmodem + + +.. |Coverage Status| image:: https://coveralls.io/repos/faucamp/python-gsmmodem/badge.png?branch=master +.. _Coverage Status: https://coveralls.io/r/faucamp/python-gsmmodem + +|Build Status|_ |Coverage Status|_ + +To run all unit tests, do:: + + python setup.py test + +Unit test code coverage information may be generated by using `coverage +`_. You can execute it directly from +setup.py by doing:: + + python setup.py coverage + +This will run all unit tests and report on code coverage statistics. + + +License information +------------------- + +Copyright (C) 2013 Francois Aucamp +See AUTHORS for all authors and contact information. + +License: GNU Lesser General Public License, version 3 or later; see COPYING + included in this archive for details. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d2794ca --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-gsmmodem.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-gsmmodem.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/python-gsmmodem" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python-gsmmodem" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..71a8995 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,16 @@ +API +=== + +GSM Modem +--------- + +.. automodule:: gsmmodem.modem + :members: + + +Serial Communications +--------------------- + +.. automodule:: gsmmodem.serial_comms + :members: + diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..0ea0f9f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,248 @@ +# -*- coding: utf-8 -*- +# +# python-gsmmodem documentation build configuration file, created by +# sphinx-quickstart on Sun Aug 11 20:50:25 2013. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'python-gsmmodem' +copyright = u'2013, Developers' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.9' +# The full version, including alpha/beta/rc tags. +release = '0.9' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'python-gsmmodemdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'python-gsmmodem.tex', u'python-gsmmodem Documentation', + u'Developers', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'python-gsmmodem', u'python-gsmmodem Documentation', + [u'Developers'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'python-gsmmodem', u'python-gsmmodem Documentation', + u'Developers', 'python-gsmmodem', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..696d89b --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,37 @@ +Examples +======== + + +Dial Callback +------------- + +.. literalinclude:: ../examples/dial_callback_demo.py + :language: python + + +Dial Polling +------------ + +.. literalinclude:: ../examples/dial_polling_demo.py + :language: python + + +Incoming Call Handling +---------------------- + +.. literalinclude:: ../examples/incoming_call_demo.py + :language: python + + +SMS Handling +------------ + +.. literalinclude:: ../examples/sms_handler_demo.py + :language: python + + +USSD Sessions +------------- + +.. literalinclude:: ../examples/ussd_demo.py + :language: python diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..4defb34 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,30 @@ +.. python-gsmmodem documentation master file, created by + sphinx-quickstart on Sun Aug 11 20:50:25 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to python-gsmmodem's documentation! +=========================================== + + +.. include:: ../README.rst + +.. automodule:: gsmmodem + + +Examples and API +================ + +.. toctree:: + :maxdepth: 3 + + examples.rst + api.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..044cf27 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,242 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\python-gsmmodem.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\python-gsmmodem.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/setup.py b/setup.py index 3dfd6a3..23d194e 100755 --- a/setup.py +++ b/setup.py @@ -115,5 +115,6 @@ def run(self): scripts=['tools/gsmterm.py', 'tools/sendsms.py', 'tools/identify-modem.py'], install_requires=requires, tests_require=tests_require, + extras_require={'docs': ['sphinx']}, cmdclass = {'test': RunUnitTests, 'coverage': RunUnitTestsCoverage}) From 9e3b115a99cde6da3321f63448b7bc3d79f71a88 Mon Sep 17 00:00:00 2001 From: David Beitey Date: Mon, 26 Aug 2013 18:27:34 +1000 Subject: [PATCH 2/4] Convert documentation over to Sphinx format; essentially this matches epydoc, just with '@' prefix changed to ':' --- docs/api.rst | 14 +++ gsmmodem/modem.py | 214 +++++++++++++++++++-------------------- gsmmodem/pdu.py | 140 ++++++++++++------------- gsmmodem/serial_comms.py | 4 +- gsmmodem/util.py | 36 +++---- 5 files changed, 211 insertions(+), 197 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 71a8995..71f673e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,6 +1,7 @@ API === + GSM Modem --------- @@ -14,3 +15,16 @@ Serial Communications .. automodule:: gsmmodem.serial_comms :members: + +PDU +--- + +.. automodule:: gsmmodem.pdu + :members: + + +Utilities +--------- + +.. automodule:: gsmmodem.util + :members: diff --git a/gsmmodem/modem.py b/gsmmodem/modem.py index 936f041..8de5879 100644 --- a/gsmmodem/modem.py +++ b/gsmmodem/modem.py @@ -164,11 +164,11 @@ def __init__(self, port, baudrate=115200, incomingCallCallbackFunc=None, smsRece def connect(self, pin=None): """ Opens the port and initializes the modem and SIM card - @param pin: The SIM card PIN code, if any - @type pin: str + :param pin: The SIM card PIN code, if any + :type pin: str - @raise PinRequiredError: if the SIM card requires a PIN but none was provided - @raise IncorrectPinError: if the specified PIN is incorrect + :raise PinRequiredError: if the SIM card requires a PIN but none was provided + :raise IncorrectPinError: if the specified PIN is incorrect """ self.log.info('Connecting to modem on port %s at %dbps', self.port, self.baudrate) super(GsmModem, self).connect() @@ -387,24 +387,24 @@ def write(self, data, waitForResponse=True, timeout=5, parseError=True, writeTer This method adds the '\r\n' end-of-line sequence to the data parameter, and writes it to the modem - @param data: Command/data to be written to the modem - @type data: str - @param waitForResponse: Whether this method should block and return the response from the modem or not - @type waitForResponse: bool - @param timeout: Maximum amount of time in seconds to wait for a response from the modem - @type timeout: int - @param parseError: If True, a CommandError is raised if the modem responds with an error (otherwise the response is returned as-is) - @type parseError: bool - @param writeTerm: The terminating sequence to append to the written data - @type writeTerm: str - @param expectedResponseTermSeq: The expected terminating sequence that marks the end of the modem's response (defaults to '\r\n') - @type expectedResponseTermSeq: str + :param data: Command/data to be written to the modem + :type data: str + :param waitForResponse: Whether this method should block and return the response from the modem or not + :type waitForResponse: bool + :param timeout: Maximum amount of time in seconds to wait for a response from the modem + :type timeout: int + :param parseError: If True, a CommandError is raised if the modem responds with an error (otherwise the response is returned as-is) + :type parseError: bool + :param writeTerm: The terminating sequence to append to the written data + :type writeTerm: str + :param expectedResponseTermSeq: The expected terminating sequence that marks the end of the modem's response (defaults to '\r\n') + :type expectedResponseTermSeq: str - @raise CommandError: if the command returns an error (only if parseError parameter is True) - @raise TimeoutException: if no response to the command was received from the modem + :raise CommandError: if the command returns an error (only if parseError parameter is True) + :raise TimeoutException: if no response to the command was received from the modem - @return: A list containing the response lines from the modem, or None if waitForResponse is False - @rtype: list + :return: A list containing the response lines from the modem, or None if waitForResponse is False + :rtype: list """ self.log.debug('write: %s', data) responseLines = SerialComms.write(self, data + writeTerm, waitForResponse=waitForResponse, timeout=timeout, expectedResponseTermSeq=expectedResponseTermSeq) @@ -446,10 +446,10 @@ def write(self, data, waitForResponse=True, timeout=5, parseError=True, writeTer def signalStrength(self): """ Checks the modem's cellular network signal strength - @raise CommandError: if an error occurs + :raise CommandError: if an error occurs - @return: The network signal strength as an integer between 0 and 99, or -1 if it is unknown - @rtype: int + :return: The network signal strength as an integer between 0 and 99, or -1 if it is unknown + :rtype: int """ csq = self.CSQ_REGEX.match(self.write('AT+CSQ')[0]) if csq: @@ -460,17 +460,17 @@ def signalStrength(self): @property def manufacturer(self): - """ @return: The modem's manufacturer's name """ + """ :return: The modem's manufacturer's name """ return self.write('AT+CGMI')[0] @property def model(self): - """ @return: The modem's model name """ + """ :return: The modem's model name """ return self.write('AT+CGMM')[0] @property def revision(self): - """ @return: The modem's software revision, or None if not known/supported """ + """ :return: The modem's software revision, or None if not known/supported """ try: return self.write('AT+CGMR')[0] except CommandError: @@ -478,24 +478,24 @@ def revision(self): @property def imei(self): - """ @return: The modem's serial number (IMEI number) """ + """ :return: The modem's serial number (IMEI number) """ return self.write('AT+CGSN')[0] @property def imsi(self): - """ @return: The IMSI (International Mobile Subscriber Identity) of the SIM card. The PIN may need to be entered before reading the IMSI """ + """ :return: The IMSI (International Mobile Subscriber Identity) of the SIM card. The PIN may need to be entered before reading the IMSI """ return self.write('AT+CIMI')[0] @property def networkName(self): - """ @return: the name of the GSM Network Operator to which the modem is connected """ + """ :return: the name of the GSM Network Operator to which the modem is connected """ copsMatch = lineMatching(r'^\+COPS: (\d),(\d),"(.+)",{0,1}\d*$', self.write('AT+COPS?')) # response format: +COPS: mode,format,"operator_name",x if copsMatch: return copsMatch.group(3) @property def supportedCommands(self): - """ @return: list of AT commands supported by this modem (without the AT prefix). Returns None if not known """ + """ :return: list of AT commands supported by this modem (without the AT prefix). Returns None if not known """ try: # AT+CLAC responses differ between modems. Most respond with +CLAC: and then a comma-separated list of commands # while others simply return each command on a new line, with no +CLAC: prefix @@ -515,7 +515,7 @@ def supportedCommands(self): @property def smsTextMode(self): - """ @return: True if the modem is set to use text mode for SMS, False if it is set to use PDU mode """ + """ :return: True if the modem is set to use text mode for SMS, False if it is set to use PDU mode """ return self._smsTextMode @smsTextMode.setter def smsTextMode(self, textMode): @@ -550,7 +550,7 @@ def _compileSmsRegexes(self): @property def smsc(self): - """ @return: The default SMSC number stored on the SIM card """ + """ :return: The default SMSC number stored on the SIM card """ if self._smscNumber == None: try: readSmsc = self.write('AT+CSCA?') @@ -576,14 +576,14 @@ def waitForNetworkCoverage(self, timeout=None): and the signal strength is greater than 0, optionally timing out if a timeout was specified - @param timeout: Maximum time to wait for network coverage, in seconds - @type timeout: int or float + :param timeout: Maximum time to wait for network coverage, in seconds + :type timeout: int or float - @raise TimeoutException: if a timeout was specified and reached - @raise InvalidStateException: if the modem is not going to receive network coverage (SIM blocked, etc) + :raise TimeoutException: if a timeout was specified and reached + :raise InvalidStateException: if the modem is not going to receive network coverage (SIM blocked, etc) - @return: the current signal strength - @rtype: int + :return: the current signal strength + :rtype: int """ block = [True] if timeout != None: @@ -624,17 +624,17 @@ def _cancelBlock(): def sendSms(self, destination, text, waitForDeliveryReport=False, deliveryTimeout=15): """ Send an SMS text message - @param destination: the recipient's phone number - @type destination: str - @param text: the message text - @type text: str - @param waitForDeliveryReport: if True, this method blocks until a delivery report is received for the sent message - @type waitForDeliveryReport: boolean - @param deliveryReport: the maximum time in seconds to wait for a delivery report (if "waitForDeliveryReport" is True) - @type deliveryTimeout: int or float + :param destination: the recipient's phone number + :type destination: str + :param text: the message text + :type text: str + :param waitForDeliveryReport: if True, this method blocks until a delivery report is received for the sent message + :type waitForDeliveryReport: boolean + :param deliveryReport: the maximum time in seconds to wait for a delivery report (if "waitForDeliveryReport" is True) + :type deliveryTimeout: int or float - @raise CommandError: if an error occurs while attempting to send the message - @raise TimeoutException: if the operation times out + :raise CommandError: if an error occurs while attempting to send the message + :raise TimeoutException: if the operation times out """ if self._smsTextMode: self.write('AT+CMGS="{0}"'.format(destination), timeout=3, expectedResponseTermSeq='> ') @@ -666,13 +666,13 @@ def sendUssd(self, ussdString, responseTimeout=15): """ Starts a USSD session by dialing the the specified USSD string, or \ sends the specified string in the existing USSD session (if any) - @param ussdString: The USSD access number to dial - @param responseTimeout: Maximum time to wait a response, in seconds + :param ussdString: The USSD access number to dial + :param responseTimeout: Maximum time to wait a response, in seconds - @raise TimeoutException: if no response is received in time + :raise TimeoutException: if no response is received in time - @return: The USSD response message/session (as a Ussd object) - @rtype: gsmmodem.modem.Ussd + :return: The USSD response message/session (as a Ussd object) + :rtype: gsmmodem.modem.Ussd """ self._ussdSessionEvent = threading.Event() try: @@ -698,13 +698,13 @@ def sendUssd(self, ussdString, responseTimeout=15): def dial(self, number, timeout=5, callStatusUpdateCallbackFunc=None): """ Calls the specified phone number using a voice phone call - @param number: The phone number to dial - @param timeout: Maximum time to wait for the call to be established - @param callStatusUpdateCallbackFunc: Callback function that is executed if the call's status changes due to + :param number: The phone number to dial + :param timeout: Maximum time to wait for the call to be established + :param callStatusUpdateCallbackFunc: Callback function that is executed if the call's status changes due to remote events (i.e. when it is answered, the call is ended by the remote party) - @return: The outgoing call - @rtype: gsmmodem.modem.Call + :return: The outgoing call + :rtype: gsmmodem.modem.Call """ if self._waitForCallInitUpdate: # Wait for the "call originated" notification message @@ -747,8 +747,8 @@ def processStoredSms(self, unreadOnly=False): This is useful if SMS messages were received during a period that python-gsmmodem was not running but the modem was powered on. - @param unreadOnly: If True, only process unread SMS messages - @type unreadOnly: boolean + :param unreadOnly: If True, only process unread SMS messages + :type unreadOnly: boolean """ states = [Sms.STATUS_RECEIVED_UNREAD] if not unreadOnly: @@ -763,15 +763,15 @@ def listStoredSms(self, status=Sms.STATUS_ALL, memory=None, delete=False): The messages are read from the memory set by the "memory" parameter. - @param status: Filter messages based on this read status; must be 0-4 (see Sms class) - @type status: int - @param memory: The memory type to read from. If None, use the current default SMS read memory - @type memory: str or None - @param delete: If True, delete returned messages from the device/SIM card - @type delete: bool + :param status: Filter messages based on this read status; must be 0-4 (see Sms class) + :type status: int + :param memory: The memory type to read from. If None, use the current default SMS read memory + :type memory: str or None + :param delete: If True, delete returned messages from the device/SIM card + :type delete: bool - @return: A list of Sms objects containing the messages read - @rtype: list + :return: A list of Sms objects containing the messages read + :rtype: list """ self._setSmsMemory(readDelete=memory) messages = [] @@ -847,14 +847,14 @@ def _handleModemNotification(self, lines): This method simply spawns a separate thread to handle the actual notification (in order to release the read thread so that the handlers are able to write back to the modem, etc) - @param lines The lines that were read + :param lines The lines that were read """ threading.Thread(target=self.__threadedHandleModemNotification, kwargs={'lines': lines}).start() def __threadedHandleModemNotification(self, lines): """ Implementation of _handleModemNotification() to be run in a separate thread - @param lines The lines that were read + :param lines The lines that were read """ for line in lines: if 'RING' in line: @@ -1018,15 +1018,15 @@ def _handleSmsStatusReport(self, notificationLine): def readStoredSms(self, index, memory=None): """ Reads and returns the SMS message at the specified index - @param index: The index of the SMS message in the specified memory - @type index: int - @param memory: The memory type to read from. If None, use the current default SMS read memory - @type memory: str or None + :param index: The index of the SMS message in the specified memory + :type index: int + :param memory: The memory type to read from. If None, use the current default SMS read memory + :type memory: str or None - @raise CommandError: if unable to read the stored message + :raise CommandError: if unable to read the stored message - @return: The SMS message - @rtype: subclass of gsmmodem.modem.Sms (either ReceivedSms or StatusReport) + :return: The SMS message + :rtype: subclass of gsmmodem.modem.Sms (either ReceivedSms or StatusReport) """ # Switch to the correct memory type if required self._setSmsMemory(readDelete=memory) @@ -1072,12 +1072,12 @@ def readStoredSms(self, index, memory=None): def deleteStoredSms(self, index, memory=None): """ Deletes the SMS message stored at the specified index in modem/SIM card memory - @param index: The index of the SMS message in the specified memory - @type index: int - @param memory: The memory type to delete from. If None, use the current default SMS read/delete memory - @type memory: str or None + :param index: The index of the SMS message in the specified memory + :type index: int + :param memory: The memory type to delete from. If None, use the current default SMS read/delete memory + :type memory: str or None - @raise CommandError: if unable to delete the stored message + :raise CommandError: if unable to delete the stored message """ self._setSmsMemory(readDelete=memory) self.write('AT+CMGD={0},0'.format(index)) @@ -1092,15 +1092,15 @@ def deleteMultipleStoredSms(self, delFlag=4, memory=None): 3: Delete All READ, SENT and UNSENT messages 4: Delete All messages (this is the default) - @param delFlag: Controls what type of messages to delete; see description above. - @type delFlag: int - @param memory: The memory type to delete from. If None, use the current default SMS read/delete memory - @type memory: str or None - @param delete: If True, delete returned messages from the device/SIM card - @type delete: bool + :param delFlag: Controls what type of messages to delete; see description above. + :type delFlag: int + :param memory: The memory type to delete from. If None, use the current default SMS read/delete memory + :type memory: str or None + :param delete: If True, delete returned messages from the device/SIM card + :type delete: bool - @raise ValueErrror: if "delFlag" is not in range [1,4] - @raise CommandError: if unable to delete the stored messages + :raise ValueErrror: if "delFlag" is not in range [1,4] + :raise CommandError: if unable to delete the stored messages """ if 0 < delFlag <= 4: self._setSmsMemory(readDelete=memory) @@ -1118,8 +1118,8 @@ def _handleUssd(self, lines): def _parseCusdResponse(self, lines): """ Parses one or more +CUSD notification lines (for USSD) - @return: USSD response object - @rtype: gsmmodem.modem.Ussd + :return: USSD response object + :rtype: gsmmodem.modem.Ussd """ if len(lines) > 1: # Issue #20: Some modem/network combinations use \r\n as in-message EOL indicators; @@ -1157,10 +1157,10 @@ def _pollCallStatus(self, expectedState, callId=None, timeout=None): """ Poll the status of outgoing calls. This is used for modems that do not have a known set of call status update notifications. - @param expectedState: The internal state we are waiting for. 0 == initiated, 1 == answered, 2 = hangup - @type expectedState: int + :param expectedState: The internal state we are waiting for. 0 == initiated, 1 == answered, 2 = hangup + :type expectedState: int - @raise TimeoutException: If a timeout was specified, and has occurred + :raise TimeoutException: If a timeout was specified, and has occurred """ callDone = False timeLeft = timeout or 999999 @@ -1209,8 +1209,8 @@ class Call(object): def __init__(self, gsmModem, callId, callType, number, callStatusUpdateCallbackFunc=None): """ - @param gsmModem: GsmModem instance that created this object - @param number: The number that is being called + :param gsmModem: GsmModem instance that created this object + :param number: The number that is being called """ self._gsmModem = weakref.proxy(gsmModem) self._callStatusUpdateCallbackFunc = callStatusUpdateCallbackFunc @@ -1240,10 +1240,10 @@ def sendDtmfTone(self, tones): Note: this is highly device-dependent, and might not work - @param digits: A str containining one or more DTMF tones to play, e.g. "3" or "*123#" + :param digits: A str containining one or more DTMF tones to play, e.g. "3" or "*123#" - @raise CommandError: if the command failed/is not supported - @raise InvalidStateException: if the call has not been answered, or is ended while the command is still executing + :raise CommandError: if the command failed/is not supported + :raise InvalidStateException: if the call has not been answered, or is ended while the command is still executing """ if self.answered: dtmfCommandBase = self.DTMF_COMMAND_BASE.format(cid=self.id) @@ -1286,10 +1286,10 @@ class IncomingCall(Call): """ Represents an incoming call, conveniently allowing access to call meta information and -control """ def __init__(self, gsmModem, number, ton, callerName, callId, callType): """ - @param gsmModem: GsmModem instance that created this object - @param number: Caller number - @param ton: TON (type of number/address) in integer format - @param callType: Type of the incoming call (VOICE, FAX, DATA, etc) + :param gsmModem: GsmModem instance that created this object + :param number: Caller number + :param ton: TON (type of number/address) in integer format + :param callType: Type of the incoming call (VOICE, FAX, DATA, etc) """ if type(callType) == str: callType = self.CALL_TYPE_MAP[callType] @@ -1304,7 +1304,7 @@ def __init__(self, gsmModem, number, ton, callerName, callId, callType): def answer(self): """ Answer the phone call. - @return: self (for chaining method calls) + :return: self (for chaining method calls) """ if self.ringing: self._gsmModem.write('ATA') @@ -1333,9 +1333,9 @@ def __init__(self, gsmModem, sessionActive, message): def reply(self, message): """ Sends a reply to this USSD message in the same USSD session - @raise InvalidStateException: if the USSD session is not active (i.e. it has ended) + :raise InvalidStateException: if the USSD session is not active (i.e. it has ended) - @return: The USSD response message/session (as a Ussd object) + :return: The USSD response message/session (as a Ussd object) """ if self.sessionActive: return self._gsmModem.sendUssd(message) diff --git a/gsmmodem/pdu.py b/gsmmodem/pdu.py index b72a7c4..306f19b 100644 --- a/gsmmodem/pdu.py +++ b/gsmmodem/pdu.py @@ -49,8 +49,8 @@ class SmsPduTzInfo(tzinfo): def __init__(self, pduOffsetStr=None): """ - @param pduOffset: 2 semi-octet timezone offset as specified by PDU (see GSM 03.40 spec) - @type pduOffset: str + :param pduOffset: 2 semi-octet timezone offset as specified by PDU (see GSM 03.40 spec) + :type pduOffset: str Note: pduOffsetStr is optional in this constructor due to the special requirement for pickling mentioned in the Python docs. It should, however, be used (or otherwise pduOffsetStr must be @@ -111,8 +111,8 @@ def decode(cls, byteIter): """ Decodes a single IE at the current position in the specified byte iterator - @return: An InformationElement (or subclass) instance for the decoded IE - @rtype: InformationElement, or subclass thereof + :return: An InformationElement (or subclass) instance for the decoded IE + :rtype: InformationElement, or subclass thereof """ iei = next(byteIter) ieLen = next(byteIter) @@ -216,10 +216,10 @@ class Pdu(object): def __init__(self, data, tpduLength): """ Constructor - @param data: the raw PDU data (as bytes) - @type data: bytearray - @param tpduLength: Length (in bytes) of the TPDU - @type tpduLength: int + :param data: the raw PDU data (as bytes) + :type data: bytearray + :param tpduLength: Length (in bytes) of the TPDU + :type tpduLength: int """ self.data = data self.tpduLength = tpduLength @@ -235,21 +235,21 @@ def __str__(self): def encodeSmsSubmitPdu(number, text, reference=0, validity=None, smsc=None, requestStatusReport=True, rejectDuplicates=False): """ Creates an SMS-SUBMIT PDU for sending a message with the specified text to the specified number - @param number: the destination mobile number - @type number: str - @param text: the message text - @type text: str - @param reference: message reference number (see also: rejectDuplicates parameter) - @type reference: int - @param validity: message validity period (absolute or relative) - @type validity: datetime.timedelta (relative) or datetime.datetime (absolute) - @param smsc: SMSC number to use (leave None to use default) - @type smsc: str - @param rejectDuplicates: Flag that controls the TP-RD parameter (messages with same destination and reference may be rejected if True) - @type rejectDuplicates: bool + :param number: the destination mobile number + :type number: str + :param text: the message text + :type text: str + :param reference: message reference number (see also: rejectDuplicates parameter) + :type reference: int + :param validity: message validity period (absolute or relative) + :type validity: datetime.timedelta (relative) or datetime.datetime (absolute) + :param smsc: SMSC number to use (leave None to use default) + :type smsc: str + :param rejectDuplicates: Flag that controls the TP-RD parameter (messages with same destination and reference may be rejected if True) + :type rejectDuplicates: bool - @return: A list of one or more tuples containing the SMS PDU (as a bytearray, and the length of the TPDU part - @rtype: list of tuples + :return: A list of one or more tuples containing the SMS PDU (as a bytearray, and the length of the TPDU part + :rtype: list of tuples """ tpduFirstOctet = 0x01 # SMS-SUBMIT PDU if validity != None: @@ -351,13 +351,13 @@ def encodeSmsSubmitPdu(number, text, reference=0, validity=None, smsc=None, requ def decodeSmsPdu(pdu): """ Decodes SMS pdu data and returns a tuple in format (number, text) - @param pdu: PDU data as a hex string, or a bytearray containing PDU octects - @type pdu: str or bytearray + :param pdu: PDU data as a hex string, or a bytearray containing PDU octects + :type pdu: str or bytearray - @raise EncodingError: If the specified PDU data cannot be decoded + :raise EncodingError: If the specified PDU data cannot be decoded - @return: The decoded SMS data as a dictionary - @rtype: dict + :return: The decoded SMS data as a dictionary + :rtype: dict """ try: pdu = toByteArray(pdu) @@ -449,7 +449,7 @@ def _decodeUserData(byteIter, userDataLen, dataCoding, udhPresent): def _decodeRelativeValidityPeriod(tpVp): """ Calculates the relative SMS validity period (based on the table in section 9.2.3.12 of GSM 03.40) - @rtype: datetime.timedelta + :rtype: datetime.timedelta """ if tpVp <= 143: return timedelta(minutes=((tpVp + 1) * 5)) @@ -466,9 +466,9 @@ def _encodeRelativeValidityPeriod(validityPeriod): """ Encodes the specified relative validity period timedelta into an integer for use in an SMS PDU (based on the table in section 9.2.3.12 of GSM 03.40) - @param validityPeriod: The validity period to encode - @type validityPeriod: datetime.timedelta - @rtype: int + :param validityPeriod: The validity period to encode + :type validityPeriod: datetime.timedelta + :rtype: int """ # Python 2.6 does not have timedelta.total_seconds(), so compute it manually #seconds = validityPeriod.total_seconds() @@ -496,11 +496,11 @@ def _encodeTimestamp(timestamp): Note: the specified timestamp must have a UTC offset set; you can use gsmmodem.util.SimpleOffsetTzInfo for simple cases - @param timestamp: The timestamp to encode - @type timestamp: datetime.datetime + :param timestamp: The timestamp to encode + :type timestamp: datetime.datetime - @return: The encoded timestamp - @rtype: bytearray + :return: The encoded timestamp + :rtype: bytearray """ if timestamp.tzinfo == None: raise ValueError('Please specify time zone information for the timestamp (e.g. by using gsmmodem.util.SimpleOffsetTzInfo)') @@ -530,11 +530,11 @@ def _decodeDataCoding(octet): def _decodeAddressField(byteIter, smscField=False, log=False): """ Decodes the address field at the current position of the bytearray iterator - @param byteIter: Iterator over bytearray - @type byteIter: iter(bytearray) + :param byteIter: Iterator over bytearray + :type byteIter: iter(bytearray) - @return: Tuple containing the address value and amount of bytes read (value is or None if it is empty (zero-length)) - @rtype: tuple + :return: Tuple containing the address value and amount of bytes read (value is or None if it is empty (zero-length)) + :rtype: tuple """ addressLen = next(byteIter) if addressLen > 0: @@ -567,11 +567,11 @@ def _decodeAddressField(byteIter, smscField=False, log=False): def _encodeAddressField(address, smscField=False): """ Encodes the address into an address field - @param address: The address to encode (phone number or alphanumeric) - @type byteIter: str + :param address: The address to encode (phone number or alphanumeric) + :type byteIter: str - @return: Encoded SMS PDU address field - @rtype: bytearray + :return: Encoded SMS PDU address field + :rtype: bytearray """ # First, see if this is a number or an alphanumeric string toa = 0x80 | 0x00 | 0x01 # Type-of-address start | Unknown type-of-number | ISDN/tel numbering plan @@ -615,8 +615,8 @@ def _encodeAddressField(address, smscField=False): def encodeSemiOctets(number): """ Semi-octet encoding algorithm (e.g. for phone numbers) - @return: bytearray containing the encoded octets - @rtype: bytearray + :return: bytearray containing the encoded octets + :rtype: bytearray """ if len(number) % 2 == 1: number = number + 'F' # append the "end" indicator @@ -626,13 +626,13 @@ def encodeSemiOctets(number): def decodeSemiOctets(encodedNumber, numberOfOctets=None): """ Semi-octet decoding algorithm(e.g. for phone numbers) - @param encodedNumber: The semi-octet-encoded telephone number (in bytearray format or hex string) - @type encodedNumber: bytearray, str or iter(bytearray) - @param numberOfOctets: The expected amount of octets after decoding (i.e. when to stop) - @type numberOfOctets: int + :param encodedNumber: The semi-octet-encoded telephone number (in bytearray format or hex string) + :type encodedNumber: bytearray, str or iter(bytearray) + :param numberOfOctets: The expected amount of octets after decoding (i.e. when to stop) + :type numberOfOctets: int - @return: decoded telephone number - @rtype: string + :return: decoded telephone number + :rtype: string """ number = [] if type(encodedNumber) in (str, bytes): @@ -657,13 +657,13 @@ def encodeGsm7(plaintext, discardInvalid=False): Encodes the specified text string into GSM-7 octets (characters). This method does not pack the characters into septets. - @param text: the text string to encode - @param discardInvalid: if True, characters that cannot be encoded will be silently discarded + :param text: the text string to encode + :param discardInvalid: if True, characters that cannot be encoded will be silently discarded - @raise ValueError: if the text string cannot be encoded using GSM-7 encoding (unless discardInvalid == True) + :raise ValueError: if the text string cannot be encoded using GSM-7 encoding (unless discardInvalid == True) - @return: A bytearray containing the string encoded in GSM-7 encoding - @rtype: bytearray + :return: A bytearray containing the string encoded in GSM-7 encoding + :rtype: bytearray """ result = bytearray() if PYTHON_VERSION >= 3: @@ -684,11 +684,11 @@ def decodeGsm7(encodedText): Decodes the specified GSM-7-encoded string into a plaintext string. - @param encodedText: the text string to encode - @type encodedText: bytearray or str + :param encodedText: the text string to encode + :type encodedText: bytearray or str - @return: A string containing the decoded text - @rtype: str + :return: A string containing the decoded text + :rtype: str """ result = [] if type(encodedText) == str: @@ -711,7 +711,7 @@ def packSeptets(octets, padBits=0): Typically the output of encodeGsm7 would be used as input to this function. The resulting bytearray contains the original GSM-7 characters packed into septets ready for transmission. - @rtype: bytearray + :rtype: bytearray """ result = bytearray() if type(octets) == str: @@ -742,13 +742,13 @@ def packSeptets(octets, padBits=0): def unpackSeptets(septets, numberOfSeptets=None, prevOctet=None, shift=7): """ Unpacks the specified septets into octets - @param septets: Iterator or iterable containing the septets packed into octets - @type septets: iter(bytearray), bytearray or str - @param numberOfSeptets: The amount of septets to unpack (or None for all remaining in "septets") - @type numberOfSeptets: int or None + :param septets: Iterator or iterable containing the septets packed into octets + :type septets: iter(bytearray), bytearray or str + :param numberOfSeptets: The amount of septets to unpack (or None for all remaining in "septets") + :type numberOfSeptets: int or None - @return: The septets unpacked into octets - @rtype: bytearray + :return: The septets unpacked into octets + :rtype: bytearray """ result = bytearray() if type(septets) == str: @@ -804,10 +804,10 @@ def encodeUcs2(text): Encodes the specified text string into UCS2-encoded bytes. - @param text: the text string to encode + :param text: the text string to encode - @return: A bytearray containing the string encoded in UCS2 encoding - @rtype: bytearray + :return: A bytearray containing the string encoded in UCS2 encoding + :rtype: bytearray """ result = bytearray() for b in map(ord, text): diff --git a/gsmmodem/serial_comms.py b/gsmmodem/serial_comms.py index a3c886a..ac445f5 100644 --- a/gsmmodem/serial_comms.py +++ b/gsmmodem/serial_comms.py @@ -25,8 +25,8 @@ class SerialComms(object): def __init__(self, port, baudrate=115200, notifyCallbackFunc=None, fatalErrorCallbackFunc=None, *args, **kwargs): """ Constructor - @param fatalErrorCallbackFunc: function to call if a fatal error occurs in the serial device reading thread - @type fatalErrorCallbackFunc: func + :param fatalErrorCallbackFunc: function to call if a fatal error occurs in the serial device reading thread + :type fatalErrorCallbackFunc: func """ self.alive = False self.port = port diff --git a/gsmmodem/util.py b/gsmmodem/util.py index d082f02..1008e5c 100644 --- a/gsmmodem/util.py +++ b/gsmmodem/util.py @@ -12,8 +12,8 @@ class SimpleOffsetTzInfo(tzinfo): def __init__(self, offsetInHours=None): """ Constructs a new tzinfo instance using an amount of hours as an offset - @param offsetInHours: The timezone offset, in hours (may be negative) - @type offsetInHours: int or float + :param offsetInHours: The timezone offset, in hours (may be negative) + :type offsetInHours: int or float """ if offsetInHours != None: #pragma: no cover self.offsetInHours = offsetInHours @@ -34,11 +34,11 @@ def parseTextModeTimeStr(timeStr): (yy = year, MM = month, dd = day, hh = hour, mm = minute, ss = second, zz = time zone [Note: the unit of time zone is a quarter of an hour]) - @param timeStr: The time string to parse - @type timeStr: str + :param timeStr: The time string to parse + :type timeStr: str - @return: datetime object representing the specified time string - @rtype: datetime.datetime + :return: datetime object representing the specified time string + :rtype: datetime.datetime """ msgTime = timeStr[:-3] tzOffsetHours = int(int(timeStr[-3:]) * 0.25) @@ -60,11 +60,11 @@ def lineMatching(regexStr, lines): Note: if you have a pre-compiled regex pattern, use lineMatchingPattern() instead - @type regexStr: Regular expression string to use - @type lines: List of lines to search + :type regexStr: Regular expression string to use + :type lines: List of lines to search - @return: the regular expression match for the first line that matches the specified regex, or None if no match was found - @rtype: re.Match + :return: the regular expression match for the first line that matches the specified regex, or None if no match was found + :rtype: re.Match """ regex = re.compile(regexStr) for line in lines: @@ -80,11 +80,11 @@ def lineMatchingPattern(pattern, lines): Note: if you are using a regex pattern string (i.e. not already compiled), use lineMatching() instead - @type pattern: Compiled regular expression pattern to use - @type lines: List of lines to search + :type pattern: Compiled regular expression pattern to use + :type lines: List of lines to search - @return: the regular expression match for the first line that matches the specified regex, or None if no match was found - @rtype: re.Match + :return: the regular expression match for the first line that matches the specified regex, or None if no match was found + :rtype: re.Match """ for line in lines: m = pattern.match(line) @@ -96,11 +96,11 @@ def lineMatchingPattern(pattern, lines): def allLinesMatchingPattern(pattern, lines): """ Like lineMatchingPattern, but returns all lines that match the specified pattern - @type pattern: Compiled regular expression pattern to use - @type lines: List of lines to search + :type pattern: Compiled regular expression pattern to use + :type lines: List of lines to search - @return: list of re.Match objects for each line matched, or an empty list if none matched - @rtype: list + :return: list of re.Match objects for each line matched, or an empty list if none matched + :rtype: list """ result = [] for line in lines: From 54ded1ee90a2367aea1f1c7e43c279c14f165774 Mon Sep 17 00:00:00 2001 From: David Beitey Date: Mon, 26 Aug 2013 18:35:10 +1000 Subject: [PATCH 3/4] Add documentation about documentation. --- README.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.rst b/README.rst index 9f214f7..054ef08 100644 --- a/README.rst +++ b/README.rst @@ -94,6 +94,22 @@ setup.py by doing:: This will run all unit tests and report on code coverage statistics. +Building documentation +---------------------- + +This package contains `Sphinx `_-based documentation. +To manually build or test the documentation locally, do the following:: + + git clone https://github.com/faucamp/python-gsmmodem.git + cd python-gsmmodem + pip install .[doc] + cd doc + make html + +For true isolation, you may wish to run the above commands within a +`virtualenv `_, which will help you manage +this development installation. + License information ------------------- From a8942ea155f73e90aa8a65d9c8d0ba452f0b8825 Mon Sep 17 00:00:00 2001 From: David Beitey Date: Mon, 26 Aug 2013 18:46:15 +1000 Subject: [PATCH 4/4] Minor syntax fix ups for reST. --- gsmmodem/modem.py | 16 ++++++++-------- gsmmodem/pdu.py | 22 +++++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/gsmmodem/modem.py b/gsmmodem/modem.py index 8de5879..c96539f 100644 --- a/gsmmodem/modem.py +++ b/gsmmodem/modem.py @@ -382,11 +382,11 @@ def _unlockSim(self, pin): raise PinRequiredError('AT+CPIN') def write(self, data, waitForResponse=True, timeout=5, parseError=True, writeTerm='\r', expectedResponseTermSeq=None): - """ Write data to the modem - - This method adds the '\r\n' end-of-line sequence to the data parameter, and - writes it to the modem - + """ Write data to the modem. + + This method adds the ``\\r\\n`` end-of-line sequence to the data parameter, and + writes it to the modem. + :param data: Command/data to be written to the modem :type data: str :param waitForResponse: Whether this method should block and return the response from the modem or not @@ -397,12 +397,12 @@ def write(self, data, waitForResponse=True, timeout=5, parseError=True, writeTer :type parseError: bool :param writeTerm: The terminating sequence to append to the written data :type writeTerm: str - :param expectedResponseTermSeq: The expected terminating sequence that marks the end of the modem's response (defaults to '\r\n') + :param expectedResponseTermSeq: The expected terminating sequence that marks the end of the modem's response (defaults to ``\\r\\n``) :type expectedResponseTermSeq: str :raise CommandError: if the command returns an error (only if parseError parameter is True) :raise TimeoutException: if no response to the command was received from the modem - + :return: A list containing the response lines from the modem, or None if waitForResponse is False :rtype: list """ @@ -1240,7 +1240,7 @@ def sendDtmfTone(self, tones): Note: this is highly device-dependent, and might not work - :param digits: A str containining one or more DTMF tones to play, e.g. "3" or "*123#" + :param digits: A str containining one or more DTMF tones to play, e.g. "3" or "\*123#" :raise CommandError: if the command failed/is not supported :raise InvalidStateException: if the call has not been answered, or is ended while the command is still executing diff --git a/gsmmodem/pdu.py b/gsmmodem/pdu.py index 306f19b..0a3b02e 100644 --- a/gsmmodem/pdu.py +++ b/gsmmodem/pdu.py @@ -136,20 +136,24 @@ def __len__(self): class Concatenation(InformationElement): """ IE that indicates SMS concatenation. - + This implementation handles both 8-bit and 16-bit concatenation indication, and exposes the specific useful details of this IE as instance variables. - + Exposes: - reference: CSMS reference number, must be same for all the SMS parts in the CSMS - parts: total number of parts. The value shall remain constant for every short - message which makes up the concatenated short message. If the value is zero then - the receiving entity shall ignore the whole information element - number: this part's number in the sequence. The value shall start at 1 and - increment for every short message which makes up the concatenated short message + + reference + CSMS reference number, must be same for all the SMS parts in the CSMS + parts + total number of parts. The value shall remain constant for every short + message which makes up the concatenated short message. If the value is zero then + the receiving entity shall ignore the whole information element + number + this part's number in the sequence. The value shall start at 1 and + increment for every short message which makes up the concatenated short message """ - + def __init__(self, iei=0x00, ieLen=0, ieData=None): super(Concatenation, self).__init__(iei, ieLen, ieData) if ieData != None: