documentation

This commit is contained in:
Michael Davidsaver
2013-04-01 09:16:59 -04:00
parent 2cc7c7ce26
commit 4facd4339a
10 changed files with 1105 additions and 23 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ include
lib
db
dbd
_build

153
documentation/Makefile Normal file
View File

@ -0,0 +1,153 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# 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 <target>' where <target> 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 " 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 " 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/devsup.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/devsup.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/devsup"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/devsup"
@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."
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."

285
documentation/conf.py Normal file
View File

@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
#
# devsup documentation build configuration file, created by
# sphinx-quickstart on Sun Mar 31 19:28:10 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.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'devsup'
copyright = u'2013, Author'
# 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 = ''
# The full version, including alpha/beta/rc tags.
release = ''
# 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 = []
# -- 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
# "<project> v<release> 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 <link> 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 = 'devsupdoc'
# -- 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', 'devsup.tex', u'devsup Documentation',
u'Author', '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', 'devsup', u'devsup Documentation',
[u'Author'], 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', 'devsup', u'devsup Documentation',
u'Author', 'devsup', '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'
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = u'devsup'
epub_author = u'Author'
epub_publisher = u'Author'
epub_copyright = u'2013, Author'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True

94
documentation/devsup.rst Normal file
View File

@ -0,0 +1,94 @@
devsup Package
==============
.. autofunction:: devsup.verinfo
:mod:`db` Module
----------------
.. module:: devsup.db
.. autofunction:: devsup.db.getRecord
:class:`Record` Class
^^^^^^^^^^^^^^^^^^^^^
.. class:: devsup.db.Record
Allows access to a single record instance.
*Record* instances can be created for any record in
the process database. However, some features will only
function when the record has Python device support.
*Record* objects allow access to fields through the
:meth:`field` method and direct access to field
values through attributes.
Attribute access is currently limited to scalar fields.
>>> R = getRecord("my:record:name")
>>> F = R.field('VAL')
>>> F.getval()
42
>>> R.VAL
42
>>> R.VAL = 43
.. automethod:: name
.. automethod:: rtype
.. automethod:: field
.. automethod:: scan
.. automethod:: asyncStart
.. automethod:: asyncFinish
.. automethod:: info
.. automethod:: infos
:class:`Field` Class
^^^^^^^^^^^^^^^^^^^^
.. autoclass:: devsup.db.Field
.. automethod:: name
.. autoattribute:: record
.. automethod:: getval
.. automethod:: putval
.. automethod:: getarray
.. automethod:: fieldinfo
.. autoclass:: devsup.db.IOScanListBlock
:members:
:inherited-members:
:undoc-members:
.. autoclass:: devsup.db.IOScanListThread
:members: add, interrupt
:mod:`hooks` Module
-------------------
.. automodule:: devsup.hooks
:members:
:undoc-members:
:show-inheritance:
:mod:`util` Module
------------------
.. automodule:: devsup.util
:members:
:undoc-members:
:show-inheritance:

24
documentation/index.rst Normal file
View File

@ -0,0 +1,24 @@
.. devsup documentation master file, created by
sphinx-quickstart on Sun Mar 31 19:28:10 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to devsup's documentation!
==================================
Contents:
.. toctree::
:maxdepth: 4
devsup
interfaces
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,149 @@
Support Modules
===============
An EPICS Record definition for most record types will
include setting the DTYP and INP or OUT fields.
An example with the longin would be: ::
record(longin, "instance:name") {
field(DTYP, "Python Device")
field(INP , "pymodule some other string")
}
This minimal example will attempt to import a Python
module named 'pymodule'. This module is expected
to provide a :func:`build` function.
:func:`build` Function
----------------------
.. function:: build(record, args)
Called when the IOC requests a device support instance
for the given record. This function should return
an object providing the methods of a
:class:`DeviceSupport`.
:param record: The :class:`Record <devsup.db.Record>` instance to which this support
will be attached. May be used to introspect and
query initial field values.
:param args: The remainder of the INP or OUT field string.
::
def build(record, args):
print 'Need device support for', record.name()
print 'Provided:', args
raise RuntimeError("Not support found!")
:class:`DeviceSupport` Interface
--------------------------------
.. class:: DeviceSupport
``DeviceSupport`` is not an actually class. Rather it is a description
of the methods which all Python device support instances must provide.
These methods will be called during the course of IOC processing.
Execptions raised by these methods will be printed to the IOC console,
but will otherwise be ignored.
The module :mod:`devsup.interfaces` provides a Zope Interface
definition by this name which may be referenced.
.. method:: process(record, reason)
:param record: :class:`Record <devsup.db.Record>` from which the request originated.
:param reason: ``None`` or an object provided when processing was requested.
This method is called whenever the associated record needs to be updated
in responce to a request. The souce of this request is typically determined
by the record's SCAN field.
The actions taken by this method will depend heavily the application.
Typically this will include reading or writing values from fields.
Record fields can be access through the provided ``Record`` instance.
.. method:: detach(record)
:param record: :class:`Record <devsup.db.Record>` from which the request originated.
Called when a device support instance is being dis-associated
from its Record. This will occur when the IOC is shutdown.
It can also occur when the INP or OUT field of a record
is modified.
No further calls to this object will be made in relation
to *record*.
.. method:: allowScan(record)
:param record: :class:`Record <devsup.db.Record>` from which the request originated.
:rtype: bool or Callable
Called when an attempt is made to set the record's SCAN field
to "I/O Intr" either at startup, or during runtime.
To permit this the return value of this function must
evaluate to *True*.
If not then the attempt will fail and SCAN will revert to
"Passive".
If a callable object is return, then it will be invoked
when SCAN is changed again, or just before :meth:`detach`
is called.
This method will typically be implemented using the
``add`` method of an I/O scan list object.
(:meth:`IOScanListBlock <devsup.db.IOScanListBlock.add>`
or :meth:`IOScanListThread <devsup.db.IOScanListThread.add>`) ::
class MySup(object):
def __init__(self):
self.a_scan = devsup.db.IOScanListThread()
def allowScan(self, record):
return self.a_scan.add(record)
Example
-------
A simple counter. The processing action is to increment the value
of the VAL field.
The following code should be placed in a file named *counter.py*
which should be placed in the Python module import path. ::
class MySupport(object):
def detach(self, record):
pass # no cleanup needed
def allowScan(self, record):
return False # I/O Intr not supported
def process(self, record, reason):
record.VAL = record.VAL + 1
try:
record.UDF = 0
except AttributeError:
pass # not all record types implement this
def build(record, args):
if not args.startswith('hello'):
raise RuntimeError('%s is not friendly.'%record)
return MySupport()
This support code can then be referenced from records. ::
record(longin, "my:int:counter") {
field(DTYP, "Python Device")
field(INP , "counter hello world")
}
record(ai, "my:float:counter") {
field(DTYP, "Raw Python Device")
field(INP , "counter hello there")
}
The following will fail to associate. ::
record(longin, "my:int:counter") {
field(DTYP, "Python Device")
field(INP , "counter do what I say")
}

190
documentation/make.bat Normal file
View File

@ -0,0 +1,190 @@
@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 ^<target^>` where ^<target^> 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. 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
)
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\devsup.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\devsup.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" == "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
)
:end

View File

@ -15,13 +15,23 @@ class _Record(object):
def __init__(self, rec):
pass
def name(self):
"""Record name
"""Record name string.
>>> R = getRecord("my:record:name")
>>> R.name()
"my:record:name"
"""
def rtype(self):
"""Record type
"""Record type name string.
>>> R = getRecord("my:record:name")
>>> R.type()
"longin"
"""
def isPyRecord(self):
"""Is this record using Python Device.
"""Is this record using Python device support.
:rtype: bool
"""
def info(self, key):
"""info(key)
@ -37,17 +47,70 @@ class _Record(object):
"""
def scan(self, sync=False, reason=None, force=0):
"""scan(sync=False)
"""Scan this record.
Scan this record. If sync is False then a
scan request is queued. If sync is True then the record
:param sync: scan in current thread (``True``), or queue (``False``).
:param reason: Reason object passed to :meth:`process <DeviceSupport.process>` (sync=True only)
:param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr)
:throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning.
If ``sync`` is False then a
scan request is queued to run in another thread..
If ``sync`` is True then the record
is scannined immidately on the current thread.
For ``reason`` argument must be used in conjunction with ``sync=True``
on records with Python device support. This provides a means
of providing extra contextual information to the record's
:meth:`process <DeviceSupport.process>` method.
``force`` is used to decide if the record will actuall be processed,
``force=0`` will only process records with SCAN=Passive.
``force=1`` will process any record if at all possible.
``force=2`` will only process records with Python device support and
SCAN=I/O Intr.
It is **never** safe to use ``sync=True`` while holding record locks,
including from within a *process* method.
"""
def asyncStart(self):
pass
"""Start asynchronous processing
This method may be called from a device support
:meth:`process <DeviceSupport.process>` method
to indicate that processing will continue
later.
.. important::
This method is **only** safe to call within a *process* method.
"""
def asyncFinish(self, reason=None):
pass
"""Indicate that asynchronous processing can complete
Similar to :meth:`scan`. Used to conclude asynchronous
process started with :meth:`asyncStart`.
Processing is completed on the current thread.
.. important::
This method should **never** be called within
a :meth:`process <DeviceSupport.process>` method,
or any other context where a Record lock is held.
Doing so will result in a deadlock.
Typically a *reason* will be passed to *process* as a way
of indicating that this is the completion of an async action. ::
AsyncDone = object()
class MySup(object):
def process(record, reason):
if reason is AsyncDone:
record.VAL = ... # store result
else:
threading.Timer(1.0, record.asyncFinish, kwargs={'reason':AsyncDone})
record.asyncStart()
"""
class _Field(object):
"""Handle for field operations
@ -59,7 +122,11 @@ class _Field(object):
def __init__(self, fld):
pass
def name(self):
"""("rec", "FLD") = name()
"""Fetch the record and field names.
>>> FLD = getRecord("rec").field("FLD")
>>> FLD.name()
("rec", "FLD")
"""
def fieldinfo(self):
"""(type, size, #elements) = fieldinfo()
@ -72,17 +139,29 @@ class _Field(object):
def getval(self):
"""Fetch the current field value as a scalar.
Returns Int, Float, or str
:rtype: int, float, or str
Returned type depends of field DBF type.
An ``int`` is returned for CHAR, SHORT, LONG, and ENUM.
A ``float`` is returned for FLOAT and DOUBLE.
A ``str`` is returned for STRING.
"""
def putval(self, val):
"""Update the field value
Must be an Int, Float or str
Must be an Int, Float or str. Strings will be truncated to 39 charactors.
"""
def getarray(self):
"""Return a numpy ndarray refering to this field for in-place operations.
The dtype of the ndarray will correspond to the field's DBF type.
Its size will be the **maximum** number of elements.
.. important::
It is only safe to read or write to this ndarray while the record
lock is held (ie withing :meth:`process <DeviceSupport.process>`).
"""
_hooks = {}

View File

@ -19,6 +19,15 @@ __all__ = [
]
def getRecord(name):
"""Retrieve a :class:`Record` instance by the
full record name.
The result is cached so the future calls will return the same instance.
This is the prefered way to get :class:`Record` instances.
>>> R = getRecord("my:record:name")
Record("my:record:name")
"""
try:
return _rec_cache[name]
except KeyError:
@ -27,12 +36,45 @@ def getRecord(name):
return rec
class IOScanListBlock(object):
"""A list of records which will be processed together.
This convienence class to handle the accounting to
maintain a list of records.
"""
def __init__(self):
super(IOScanListBlock,self).__init__()
self._recs, self._recs_add, self._recs_remove = set(), set(), set()
self.force, self._running = 2, False
def add(self, rec):
"""Add a record to the scan list.
This method is designed to be consistent
with :meth:`allowScan <DeviceSupport.allowScan>`
by returning its :meth:`remove` method.
If fact this function can be completely delegated. ::
class MyDriver(util.StoppableThread):
def __init__(self):
super(MyDriver,self).__init__()
self.lock = threading.Lock()
self.scan1 = IOScanListBlock()
def run(self):
try:
while self.shouldRun():
time.sleep(1)
with self.lock:
self.scan1.interrupt()
finally:
self.finish()
class MySup(object):
def __init__(self, driver):
self.driver = driver
def allowScan(rec):
with self.driver.lock:
return self.driver.scan1.add(rec)
"""
assert isinstance(rec, Record)
if self._running:
@ -45,6 +87,8 @@ class IOScanListBlock(object):
return self.remove
def remove(self, rec):
"""Remove a record from the scan list.
"""
if self._running:
self._recs_add.discard(rec)
self._recs_add._recs_remove(rec)
@ -53,6 +97,15 @@ class IOScanListBlock(object):
self._recs.discard(rec)
def interrupt(self, reason=None, mask=None):
"""Scan the records in this list.
:param reason: Passed to :meth:`Record.scan`.
:param mask: A *list* or *set* or records which should not be scanned.
This method will call :meth:`Record.scan` of each of the records
currently in the list. This is done synchronously in the current
thread. It should **never** be call when any record locks are held.
"""
self._running = True
try:
for R in self._recs:
@ -75,6 +128,10 @@ def _default_whendone(type, val, tb):
traceback.print_exception(type, val, tb)
class IOScanListThread(IOScanListBlock):
"""A list of records w/ a worker thread to run them.
All methods are thread-safe.
"""
_worker = None
_worker_lock = threading.Lock()
queuelength=100
@ -95,6 +152,28 @@ class IOScanListThread(IOScanListBlock):
self._lock = threading.Lock()
def add(self, rec):
"""Add a record to the scan list.
This method is thread-safe and may be used
without additional locking. ::
class MyDriver(util.StoppableThread):
def __init__(self):
super(MyDriver,self).__init__()
self.scan1 = IOScanListThread()
def run(self):
try:
while self.shouldRun():
time.sleep(1)
self.scan1.interrupt()
finally:
self.finish()
class MySup(object):
def __init__(self, driver):
self.driver = driver
self.allowScan = self.driver.scan1.add
"""
with self._lock:
return super(IOScanListThread,self).add(rec)
@ -103,12 +182,26 @@ class IOScanListThread(IOScanListBlock):
return super(IOScanListThread,self).remove(rec)
def interrupt(self, reason=None, mask=None, whendone=_default_whendone):
"""Queue a request to process the scan list.
:param reason: Passed to :meth:`Record.scan`.
:param mask: A *list* or *set* or records which should not be scanned.
:param whendone: A callable which will be invoked after all records are processed.
:throws: RuntimeError is the request can't be queued.
Calling this method will cause a request to be sent to a
worker thread. This method can be called several times
to queue several requests.
If provided, the *whendone* callable is invoked with three arguments.
These will be None except in the case an interrupt is raised in the
worker in which case they are: exception type, value, and traceback.
.. note::
This method may be safely called while record locks are held.
"""
W = self.getworker()
try:
W.add(self._X, (reason, mask, whendone))
return True
except RuntimeError:
return False
W.add(self._X, (reason, mask, whendone))
def _X(self, reason, mask, whendone):
try:
@ -127,18 +220,26 @@ class Record(_dbapi._Record):
def field(self, name):
"""Lookup field in this record
fld = rec.field('HOPR')
:rtype: :class:`Field`
:throws: KeyError for non-existant fields.
The returned object is cached so future calls will
return the same instance.
>>> getRecord("rec").field('HOPR')
Field("rec.HOPR")
"""
try:
F = self._fld_cache[name]
if F is _no_such_field:
raise ValueError()
return F
except KeyError:
except KeyError, e:
try:
fld = Field("%s.%s"%(self.name(), name))
except ValueError:
self._fld_cache[name] = _no_such_field
raise e
else:
self._fld_cache[name] = fld
return fld
@ -166,7 +267,7 @@ class Record(_dbapi._Record):
class Field(_dbapi._Field):
@property
def record(self):
"""Fetch the record associated with this field
"""Fetch the :class:`Record` associated with this field
"""
try:
return self._record

View File

@ -13,11 +13,17 @@ hooknames = _dbapi._hooks.keys()
def addHook(state, func):
"""addHook("stats", funcion)
Add callback function to IOC start sequence.
Add callable to IOC start sequence.
def show():
print 'State Occurred'
addHook("AfterIocRunning", show)
Callables are run in the reverse of the order in
which they were added.
>>> def show():
... print 'State Occurred'
>>> addHook("AfterIocRunning", show)
An additional special hook 'AtIocExit' may be used
for cleanup actions during IOC shutdown.
"""
sid = _dbapi._hooks[state]
try: