Commits

Marcus von Appen  committed 5d38432

- added basic documentation
- renamed font module to sysfont

  • Participants
  • Parent commits 71ff975

Comments (0)

Files changed (21)

 syntax: glob
+*settings
 *project
 *.pyc
 *~
 *.class
 *.core
+doc/_build
 PYTHONPATH ?= $(top_srcdir)
 SUBDIRS = \
 	$(top_srcdir)/test/util \
-	$(top_srcdir)/test
+	$(top_srcdir)/test \
+	$(top_srcdir)/doc
 
 clean:
 	@echo "Cleaning up in $(top_srcdir)/ ..."
 ---
 A simple component-based framework.
 
-font
-----
+sysfont
+-------
 System font handling functions.
 
 
     recommended to be platform neutral, since it is shown to the user on
     errors. libnames can be a list of shared library names or a dictionary
     consisting of platform->library name mappings. path is the explicit library
-    path to be used, if any. In case the library is not found in path, ctypes'
-    standard mechanism for finding libraries (ctypes.util.find_library) is
-    used.
+    path to be used, if any.  path acts as the first location to be used for
+    loading the library, before the standard mechanisms of ctypes will be used.
     """
     def __init__(self, libinfo, libnames, path=None):
         self._dll = None

File doc/Makefile

+# 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)/*
+	@rm -f modules/*~ tutorial/*~ *~
+
+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/PySDL2.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PySDL2.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/PyMule"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyMule"
+	@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."

File doc/array.rst

+.. module:: array
+   :synopsis: Conversion routines for sequences.
+
+array - Converting sequences
+============================
+This module provides various functions and classes to access sequences and
+buffer-style objects in different ways. It also provides conversion routines
+to improve the interoperability of sequences with :mod:`ctypes` data types.
+
+Providing read-write access for sequential data
+-----------------------------------------------
+Two classes allow you to access sequential data in different ways. The
+:class:`CTypesView` provides byte-wise access to iterable objects and allows
+you to convert the object representation to matching byte-widths for
+:mod:`ctypes` or other modules.
+
+Depending on the the underlying object and the chosen size of each particular
+item of the object, the :class:`CTypesView` allows you to operate directly
+on different representations of the object's contents. ::
+
+    >>> text = bytearray("Hello, I am a simple ASCII string!")
+    >>> ctview = CTypesView(text, itemsize=1)
+    >>> ctview.view[0] = 0x61
+    >>> print(text)
+    aello, I am a simple ASCII string!"
+    >>> ctview.to_uint16()[3] = 0x6554
+    >>> print(text)
+    aello,Te am a simple ASCII string!"
+
+The snippet above provides a single-byte sized view on a :func:`bytearray`
+object. Afterwards, the first item of the view is changed, which causes a
+change on the :func:`bytearray`, on the first item as well, since both, the
+:class:`CTypesView` and the :func:`bytearray` provide a byte-wise access to
+the contents.
+
+By using :meth:`CTypesView.to_uint16()`, we change the access representation to
+a 2-byte unsigned integer :mod:`ctypes` pointer and change the fourth 2-byte
+value, *I* to something else. ::
+
+    >>> text = bytearray("Hello, I am a simple ASCII string!")
+    >>> ctview = CTypesView(text, itemsize=2)
+    >>> ctview.view[0] = 0x61
+    >>> print(text)
+    aello, I am a simple ASCII string!"
+    >>> ctview.to_uint16()[3] = 0x6554
+    >>> print(text)
+    aello,Te am a simple ASCII string!"
+
+If the encapsuled object does not provide a (writeable) :func:`buffer`
+interface, but is iterable, the :class:`CTypesView` will create an
+internal copy of the object data using Python's :mod:`array` module and
+perform all operations on that copy. ::
+
+    >>> mylist = [18, 52, 86, 120, 154, 188, 222, 240]
+    >>> ctview = CTypesView(mylist, itemsize=1, docopy=True)
+    >>> print(ctview.object)
+    array('B', [18, 52, 86, 120, 154, 188, 222, 240])
+    >>> ctview.view[3] = 0xFF
+    >>> print(mylist)
+    [18, 52, 86, 120, 154, 188, 222, 240]
+    >>> print(ctview.object)
+    array('B', [18, 52, 86, 255, 154, 188, 222, 240])
+
+As for directly accessible objects, you can define your own itemsize to
+be used. If the iterable does not provide a direct byte access to their
+contents, this won't have any effect except for resizing the item
+widths. ::
+
+    >>> mylist = [18, 52, 86, 120, 154, 188, 222, 240]
+    >>> ctview = CTypesView(mylist, itemsize=4, docopy=True)
+    >>> print(ctview.object)
+    array('I', [18L, 52L, 86L, 120L, 154L, 188L, 222L, 240L])
+
+Accessing data over multiple dimensions
+---------------------------------------
+The second class, :class:`MemoryView` provides an interface to access
+data over multiple dimensions. You can layout and access a simple
+byte stream over e.g. two or more axes, providing a greater flexibility
+for functional operations and complex data.
+
+Let's assume, we are reading image data from a file stream into some buffer
+object and want to access and manipulate the image data. Images feature two
+axes, one being the width, the other being the height, defining a rectangular
+graphics area.
+
+When we read all data from the file, we have an one-dimensional view of the
+image graphics. The :class:`MemoryView` allows us to define a
+two-dimensional view over the image graphics, so that we can operate on
+both, rows and columns of the image. ::
+
+    >>> imagedata = bytearray("some 1-byte graphics data")
+    >>> view = MemoryView(imagedata, 1, (5, 5))
+    >>> print(view)
+    [[s, o, m, e,  ], [1, -, b, y, t], [e,  , g, r, a], [p, h, i, c, s], [ , d, a, t, a]]
+    >>> for row in view:
+    ...     print(row)
+    ...
+    [s, o, m, e,  ]
+    [1, -, b, y, t]
+    [e,  , g, r, a]
+    [p, h, i, c, s]
+    [ , d, a, t, a]
+    >>> for row in view:
+    ...    row[1] = "X"
+    ...    print row
+    ...
+    [s, X, m, e,  ]
+    [1, X, b, y, t]
+    [e, X, g, r, a]
+    [p, X, i, c, s]
+    [ , X, a, t, a]
+    >>> print(imagedata)
+    sXme 1XbyteXgrapXics Xata
+
+On accessing a particular dimension of a :class:`MemoryView`, a new
+:class:`MemoryView` is created, if it does not access a single
+element. ::
+
+    >>> firstrow = view[0]
+    >>> type(firstrow)
+    <class 'mule.array.MemoryView'>
+    >>> type(firstrow[0])
+    <type 'bytearray'>
+
+A :class:`MemoryView` features, similar to Python's builtin
+:class:`memoryview`, dimensions and strides, accessible via the
+:attr:`MemoryView.ndim` and :attr:`MemoryView.strides` attributes.
+
+    >>> view.ndim
+    2
+    >>> view.strides
+    (5, 5)
+
+The :attr:`MemoryView.strides`, which have to be passed on creating a
+new :class:`MemoryView`, define the layout of the data over different
+dimensions. In the example above, we created a 5x5 two-dimensional view
+to the image graphics. ::
+
+    >>> twobytes = MemoryView(imagedata, 2, (5, 1))
+    >>> print(twobytes)
+    [[sX, me,  1, Xb, yt], [eX, gr, ap, Xi, cs]]
+
+
+API
+---
+
+.. class:: CTypesView(obj : iterable[, itemsize=1[, docopy=False[, objsize=None]]])
+
+   A proxy class for byte-wise accessible data types to be used in
+   ctypes bindings. The CTypesView provides a read-write access to
+   arbitrary objects that are iterable.
+
+   In case the object does not provide a :func:`buffer()` interface for
+   direct access, the CTypesView can copy the object's contents into an
+   internal buffer, from which data can be retrieved, once the necessary
+   operations have been performed.
+
+   Depending on the item type stored in the iterable object, you might
+   need to provide a certain *itemsize*, which denotes the size per
+   item in bytes. The *objsize* argument might be necessary of iterables,
+   for which len() does not return the correct amount of objects or is not
+   implemented.
+
+   .. attribute:: bytesize
+
+      Returns the length of the encapsuled object in bytes.
+
+   .. attribute:: is_shared
+
+      Indicates, if changes on the CTypesView data effect the encapsuled
+      object directly. if not, this means that the object was copied
+      internally and needs to be updated by the user code outside of the
+      CTypesView.
+
+   .. attribute:: object
+
+      The encapsuled object.
+
+   .. attribute:: view
+
+      Provides a read-write aware view of the encapsuled object data
+      that is suitable for usage from :mod:`ctypes`.
+
+   .. method:: to_bytes() -> ctypes.POINTER
+
+      Returns a byte representation of the encapsuled object. The return
+      value allows a direct read-write access to the object data, if it
+      is not copied. The :func:`ctypes.POINTER` points to an array of
+      :class:`ctypes.c_ubyte`.
+
+   .. method:: to_uint16() -> ctypes.POINTER
+
+      Returns a 16-bit representation of the encapsuled object. The return
+      value allows a direct read-write access to the object data, if it
+      is not copied. The :func:`ctypes.POINTER` points to an array of
+      :class:`ctypes.c_ushort`.
+
+   .. method:: to_uint32() -> ctypes.POINTER
+
+      Returns a 32-bit representation of the encapsuled object. The return
+      value allows a direct read-write access to the object data, if it
+      is not copied. The :func:`ctypes.POINTER` points to an array of
+      :class:`ctypes.c_uint`.
+
+   .. method:: to_uint64() -> ctypes.POINTER
+
+      Returns a 64-bit representation of the encapsuled object. The return
+      value allows a direct read-write access to the object data, if it
+      is not copied. The :func:`ctypes.POINTER` points to an array of
+      :class:`ctypes.c_ulonglong`.
+
+.. class:: MemoryView(source : object, itemsize : int, strides : tuple[, getfunc=None[, setfunc=None[, srcsize=None]]])
+
+   The :class:`MemoryView` provides a read-write access to arbitrary
+   data objects, which can be indexed.
+
+   *itemsize* denotes the size of a single item. *strides* defines
+   the dimensions and the length (n items * *itemsize*) for each
+   dimension. *getfunc* and *setfunc* are optional parameters to
+   provide specialised read and write access to the underlying
+   *source*. *srcsize* can be used to provide the correct source
+   size, if ``len(source)`` does not return the absolute size of the
+   source object in all dimensions.
+
+   .. note::
+
+      The MemoryView is a pure Python-based implementation and makes
+      heavy use of recursion for multi-dimensional access. If you aim
+      for speed on accessing a n-dimensional object, you want to
+      consider using a specialised library such as numpy. If you need
+      n-dimensional access support, where such a library is not
+      supported, or if you need to provide access to objects, which do
+      not fulfill the requirements of that particular libray,
+      :class:`MemoryView` can act as solid fallback solution.
+
+   .. attribute:: itemsize
+
+      The size of a single item in bytes.
+
+   .. attribute:: ndim
+
+      The number of dimensions of the :class:`MemoryView`.
+
+   .. attribute:: size
+
+      The size in bytes of the underlying source object.
+
+   .. attribute:: source
+
+      The underlying data source.
+
+   .. attribute:: strides
+
+      A tuple defining the length in bytes for accessing all
+      elements in each dimension of the :class:`MemoryView`.
+
+.. function:: to_ctypes(dataseq : iterable, dtype[, mcount=0]) -> array, int
+
+    Converts an arbitrary sequence to a ctypes array of the specified
+    *dtype* and returns the ctypes array and amount of items as
+    two-value tuple.
+
+    Raises a :exc:`TypeError`, if one or more elements in the passed
+    sequence do not match the passed *dtype*.
+
+.. function:: to_list(dataseq : iterable) -> list
+
+   Converts a ctypes array to a list.
+
+.. function:: to_tuple(dataseq : iterable) -> tuple
+
+   Converts a ctypes array to a tuple.
+
+.. function:: create_array(obj : object, itemsize : int) -> array.array
+
+   Creates an :class:`array.array` based copy of the passed object.
+   *itemsize* denotes the size in bytes for a single element within
+   *obj*.

File doc/compat.rst

+.. module:: compat
+   :synopsis: Python compatibility helpers.
+
+compat - Python compatibility helpers
+=====================================
+The :mod:`compat` module is for internal purposes of your package or
+application and should not be used outside of it.
+
+.. data:: ISPYTHON2
+
+   ``True``, if executed in a Python 2.x compatible interpreter, ``False``
+   otherwise.
+
+.. data:: ISPYTHON3
+
+   ``True``, if executed in a Python 3.x compatible interpreter, ``False``
+   otherwise.
+
+.. function:: long([x[, base]])
+
+   .. note::
+
+      Only defined for Python 3.x, for which it is the same as :func:`int()`.
+
+.. function:: unichr(i)
+
+   .. note::
+
+      Only defined for Python 3.x, for which it is the same as :func:`chr()`.
+
+.. function:: callable(x) -> bool
+
+   .. note::
+
+      Only defined for Python 3.x, for which it is the same as
+      ``isinstance(x, collections.Callable)``
+
+.. function:: byteify(x : string, enc : string) -> bytes
+
+   Converts a string to a :func:`bytes` object.
+
+.. function:: stringify(x : bytes, enc : string) -> string
+
+   Converts a :func:`bytes` to a string object.
+
+.. function:: isiterable(x) -> bool
+
+   Shortcut for ``isinstance(x, collections.Iterable)``.
+
+.. function:: platform_is_64bit() -> bool
+
+   Checks, if the interpreter is 64-bit capable.
+
+.. decorator:: deprecated
+
+   A simple decorator to mark functions and methods as deprecated. This will
+   print a deprecation message each time the function or method is invoked.
+
+.. function:: deprecation(message : string) -> None
+
+   Prints a deprecation message using the :meth:`warnings.warn()` method.
+
+.. exception:: UnsupportedError(obj : object[, msg=None])
+
+   Indicates that a certain class, function or behaviour is not supported in
+   the specific execution environment.
+
+.. decorator:: experimental
+
+   A simple decorator to mark functions and methods as
+   experimental. This will print a warning each time the function or
+   method is invoked.
+
+.. exception:: ExperimentalWarning(obj : object[, msg=None])
+
+   Indicates that a certain class, function or behaviour is in an
+   experimental state.
+# -*- coding: utf-8 -*-
+#
+# PySDL2 documentation build configuration file, created by
+# sphinx-quickstart on Thu Mar 22 07:51:57 2012.
+#
+# 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.intersphinx',
+              'sphinx.ext.todo',
+              'sphinx.ext.coverage',
+              'sphinx.ext.inheritance_diagram'
+              ]
+
+todo_include_todos = True
+graphviz_output_format = 'png'
+
+# 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 Utilities'
+copyright = u'2013, Marcus von Appen'
+
+# 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.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1.0'
+
+# 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 = 'PythonUtilitiesDoc'
+
+
+# -- 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', 'PythonUtilities.tex', u'Python Utilities Documentation',
+   u'Marcus von Appen', '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', 'pythonutils', u'Python Utilities Documentation',
+     [u'Marcus von Appen'], 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', 'pythonutils', u'Python Utilities Documentation',
+   u'Marcus von Appen', 'Python Utilities', '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'
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'http://docs.python.org/': "python.inv"}
+.. module:: dll
+   :synopsis: DLL loading
+
+dll - DLL loading
+=================
+
+The :mod:`dll` module is not intended for consumers of your specific
+application or library. It is a helper module for loading the 3rd party
+libraries used by your project itself.
+
+.. class:: DLL(libinfo : string, libnames : string or dict[, path=None])
+
+   A simple wrapper class for loading shared libraries through ctypes.
+
+   The *libinfo* argument is a descriptive name of the library, that is
+   recommended to be platform neutral, since it is shown to the user on
+   errors. *libnames* can be a list of shared library names or a dictionary
+   consisting of platform->library name mappings. *path* is the explicit
+   library path to be used, if any. *path* acts as the first location to be
+   used for loading the library, before the standard mechanisms of
+   :mod:`ctypes` will be used  
+   
+   .. attribute:: libfile
+
+      Gets the filename of the loaded library.
+
+   .. method:: bind_function(funcname : string[, args=None[, returns=None[,optfunc=None]]]) -> function
+
+      Tries to resolve the passed function name and, if found, binds the
+      list of *args*, to its ``argtypes`` and the *returns* value to its
+      ``restype``. If the function is not found, *optfunc* will be used
+      instead, without the assignment of *args* and *returns*.
+.. module:: ebs
+   :synopsis: A component-based entity system framework.
+
+ebs - A component-based entity system framework
+===============================================
+This module loosely follows a component oriented pattern to separate
+object instances, carried data and processing logic within applications
+or games. It uses an entity based approach, in which object instances are
+unique identifiers, while their data is managed within components, which
+are separately stored. For each individual component type a processing
+system will take care of all necessary updates on running the application.
+
+Component-based patterns
+------------------------
+Component-based means that - instead of a traditional OOP approach - object
+information are split up into separate data bags for reusability and that those
+data bags are separated from any application logic.
+
+Behavioural design
+^^^^^^^^^^^^^^^^^^
+Imagine a car game class in traditional OOP, which might look like ::
+
+   class Car:
+       def __init__(self):
+           self.color = "red"
+           self.position = 0, 0
+           self.velocity = 0, 0
+           self.sprite = get_some_car_image()
+           ...
+       def drive(self, timedelta):
+           self.position[0] = self.velocity[0] * timedelta
+           self.position[1] = self.velocity[1] * timedelta
+           ...
+       def stop(self):
+           self.velocity = 0, 0
+           ...
+       def render(self, screen):
+           screen.display(self.sprite)
+
+   mycar = new Car()
+   mycar.color = "green"
+   mycar.velocity = 10, 0
+
+The car features information stored in attributes (``color``, ``position``,
+...) and behaviour (application logic, ``drive()``, ``stop()`` ...).
+
+A component-based approach aims to split and reduce the car to a set of
+information and external systems providing the application logic. ::
+
+   class Car:
+       def __init__(self):
+           self.color = "red"
+           self.position = 0, 0
+           self.velocity = 0, 0
+           self.sprite = get_some_car_image()
+
+   class CarMovement:
+       def drive(self, car, timedelta):
+           car.position[0] = car.velocity[0] * timedelta
+           car.position[1] = car.velocity[1] * timedelta
+           ...
+       def stop(self):
+           car.velocity = 0, 0
+
+   class CarRenderer:
+       def render(self, car, screen):
+           screen.display(car.sprite)
+
+At this point of time, there is no notable difference between both approaches,
+except that the latter one adds additional overhead.
+
+The benefit comes in, when you
+
+* use subclassing in your OOP design
+* want to change behavioural patterns on a global scale or based on states
+* want to refactor code logic in central locations
+* want to cascade application behaviours
+
+The initial ``Car`` class from above defines, how it should be displayed
+on the screen. If you now want to add a feature for rescaling the screen
+size after the user activates the magnifier mode, you need to refactor
+the ``Car`` and all other classes that render things on the screen, have
+to consider all subclasses that override the method and so on.
+Refactoring the ``CarRenderer`` code by adding a check for the magnifier
+mode sounds quite simple in contrast to that, not?
+
+The same applies to the movement logic - inverting the movement logic
+requires you to refactor all your classes instead of a single piece of
+application code.
+
+Information design
+^^^^^^^^^^^^^^^^^^
+Subclassing with traditional OOP for behavioural changes also might
+bloat your classes with unnecessary information, causing the memory
+footprint for your application to rise without any need. Let's assume
+you have a ``Truck`` class that inherits from ``Car``. Let's further
+assume that all trucks in your application look the same. Why should any
+of those carry a ``sprite`` or ``color`` attribute? You would need to
+refactor your ``Car`` class to get rid of those superfluous information,
+adding another level of subclassing. If at a later point of time you
+decide to give your trucks different colors, you need to refactor
+everything again.
+
+Wouldn't it be easier to deal with colors, if they are available on the
+truck and leave them out, if they are not? We initially stated that the
+component-based approach aims to separate data (information) from code
+logic.  That said, if the truck has a color, we can handle it easily, if
+it has not, we will do as usual.
+
+Also, checking for the color of an object (regardless, if it is a truck,
+car, airplane or death star) allows us to apply the same or similar
+behaviour for every object. If the information is available, we will
+process it, if it is not, we will not do anything.
+
+All in all
+^^^^^^^^^^
+Once we split up the previously OOP-style classes into pure data containers and
+some separate processing code for the behaviour, we are talking about components
+and (processing) systems. A component is a data container, ideally grouping
+related information on a granular level, so that it is easy to (re)use.
+When you combine different components to build your in-application objects and
+instantiate those, we are talking about entities.
+
+.. image:: images/ebs.png
+
+*Component*
+   provides information (data bag)
+
+*Entity*
+   In-application instance that consists of *component* items
+
+*System*
+   Application logic for working with *Entity* items and their
+   *component* data
+
+*World*
+   The environment that contains the different *System* instances and
+   all *Entity* items with their *component* data
+
+Within a strict COP design, the application logic (ideally) only knows about
+data to process. It does not know anything about entities or complex classes
+and only operates on the data.
+
+.. image:: images/copprocessing.png
+
+To keep things simple, modular and easy to maintain and change, you usually
+create small processing systems, which perform the necessary operations on the
+data they shall handle. That said, a ``MovementSystem`` for our car entity would
+only operate on the position and velocity component of the car entity. It does
+not know anything about the the car's sprite or sounds that the car makes,
+since *this is nothing it has to deal with*.
+
+To display the car on the screen, a ``RenderSystem`` might pick up the sprite
+component of the car, maybe along with the position information (so it know,
+where to place the sprite) and render it on the screen.
+
+If you want the car to play sounds, you would add an audio playback system,
+that can perform the task. Afterwards you can add the necessary audio
+information via a sound component to the car and it will make noise.
+
+Component-based design with ebs
+-------------------------------
+
+.. note::
+
+   This section will deal with the specialities of COP patterns and provides
+   the bare minimum of information.
+   
+:mod:`ebs` provides a :class:`World` class in which all other objects
+will reside. The :class:`World` will maintain both, :class:`Entity` and
+component items, and allows you to set up the processing logic via
+the :class:`System` and :class:`Applicator` classes. ::
+
+   >>> appworld = World()
+
+Components can be created from any class that inherits from the
+:class:`object` type and represent the data bag of information for the
+entity. and application world. Ideally, they should avoid any
+application logic (except from getter and setter properties). ::
+
+   class Position2D(object):
+       def __init__(self, x=0, y=0):
+           self.x = x
+           self.y = y
+
+:class:`Entity` objects define the in-application objects and only consist of
+component-based attributes. They also require a :class:`World` at
+object instantiation time. ::
+
+   class CarEntity(Entity):
+       def __init__(self, world, x=0, y=0):
+           self.position2d = Position2D(x, y)
+
+.. note::
+
+   The *world* argument in ``__init__()`` is necessary. It will be
+   passed to the internal ``__new__()`` constructor of the
+   :class:`Entity` and stores a reference to the :class:`World` and also
+   allows the :class:`Entity` to store its information in the
+   :class:`World`.
+
+The :class:`Entity` also requries its attributes to be named exactly as
+their component class name, but in lowercase letters. If you name a
+component ``MyAbsolutelyAwesomeDataContainer``, an :class:`Entity` will
+force you to write the following: ::
+
+   class SomeEntity(Entity):
+       def __init__(self, world):
+           self.myabsolutelyawesomedatacontainer = MyAbsolutelyAwesomeDataContainer()
+
+.. note::
+
+   This is not entirely true. A reference of the object will be stored on a
+   per-class-in-mro basis. This means that if ``MyAbsolutelyAwesomeDataContainer``
+   inherits from ``ShortName``, you can also do: ::
+
+     class SomeEntity(Entity):
+         def __init__(self, world):
+             self.shortname = MyAbsolutelyAwesomeDataContainer()
+
+Components should be as atomic as possible and avoid complex
+inheritance. Since each value of an :class:`Entity` is stored per class
+in its mro list, components inheriting from the same class(es) will
+overwrite each other on conflicting classes: ::
+
+   class Vector(Position2D):
+       def __init__(self, x=0, y=0, z=0):
+           super(Vector, self).__init__(x, y)
+
+   
+   class SomeEntity(Entity):
+       def __init__(self, world):
+           # This will associate self.position2d with the new Position2D
+           # value, while the previous Vector association is overwritten
+           self.position2d = Position2D(4, 4)
+           
+           # self.vector will also associate a self.position2d attribute
+           # with the Entity, since Vector inherits from Position2D. The
+           # original association will vanish, and each call to
+           # entity.position2d will effectively manipulate the vector!
+           self.vector = Vector(1,2,3)
+
+API
+---
+
+.. class:: Entity(world : World)
+
+   An entity is a specific object living in the application world. It
+   does not carry any data or application logic, but merely acts as
+   identifier label for data that is maintained in the application
+   world itself.
+   
+   As such, it is an composition of components, which would not exist
+   without the entity identifier. The entity itself is non-existent to
+   the application world as long as it does not carry any data that can
+   be processed by a system within the application world.
+
+   .. attribute:: id
+
+      The id of the Entity. Every Entity has a unique id, that is
+      represented by a :class:`uuid.UUID` instance.
+
+   .. attribute:: world
+
+      The :class:`World` the entity resides in.
+
+   .. method:: delete() -> None
+
+      Deletes the :class:`Entity` from its :class:`World`. This
+      basically calls :meth:`World.delete()` with the :class:`Entity`.
+
+.. class:: Applicator()
+
+   A processing system for combined data sets. The :class:`Applicator`
+   is an enhanced :class:`System` that receives combined data sets based
+   on its set :attr:`System.componenttypes`
+
+   .. attribute:: is_applicator
+   
+      A boolean flag indicating that this class operates on combined data sets.
+   
+   .. attribute:: componenttypes
+
+      A tuple of class identifiers that shall be processed by the
+      :class:`Applicator`.
+
+   .. function:: process(world : World, componentsets : iterable)
+
+      Processes tuples of component items. ``componentsets`` will
+      contain object tuples, that match the :attr:`componenttypes`
+      of the :class:`Applicator`. If, for example, the :class:`Applicator`
+      is defined as ::
+
+        class MyApplicator(Applicator):
+            def __init__(self):
+                self.componenttypes = (Foo, Bar)
+
+      its process method will receive ``(Foo, Bar)`` tuples ::
+
+            def process(self, world, componentsets):
+                for foo_item, bar_item in componentsets:
+                    ...
+
+      Additionally, the :class:`Applicator` will not process all possible
+      combinations of valid components, but only those, which are associated
+      with the same :class:`Entity`. That said, an :class:`Entity` *must*
+      contain a ``Foo`` as well as a ``Bar`` component in order to
+      have them both processed by the :class:`Applicator` (while a
+      :class:`System` with the same ``componenttypes`` would pick either of
+      them, depending on their availability).
+
+.. class:: System()
+
+   A processing system within an application world consumes the
+   components of all entities, for which it was set up. At time of
+   processing, the system does not know about any other component type
+   that might be bound to any entity.
+
+   Also, the processing system does not know about any specific entity,
+   but only is aware of the data carried by all entities.
+
+   .. attribute:: componenttypes
+
+      A tuple of class identifiers that shall be processed by the
+      :class:`System`
+
+   .. method:: process(world : World, components : iterable)
+
+      Processes component items.
+
+      This method has to be implemented by inheriting classes.
+
+
+.. class:: World()
+
+   An application world defines the combination of application data and
+   processing logic and how the data will be processed. As such, it is a
+   container object in which the application is defined.
+
+   The application world maintains a set of entities and their related
+   components as well as a set of systems that process the data of the
+   entities. Each processing system within the application world only
+   operates on a certain set of components, but not all components of an
+   entity at once.
+
+   The order in which data is processed depends on the order of the
+   added systems.
+
+   .. attribute:: systems
+
+      The processing system objects bound to the world.
+
+   .. method:: add_system(system : object)
+
+      Adds a processing system to the world. The system will be
+      added as last item in the processing order.
+      
+      The passed system does not have to inherit from :class:`System`, but
+      must feature a ``componenttypes`` attribute and a ``process()`` method,
+      which match the signatures of the :class:`System` class ::
+      
+        class MySystem(object):
+            def __init__(self):
+                # componenttypes can be any iterable as long as it
+                # contains the classes the system should take care of
+                self.componenttypes = [AClass, AnotherClass, ...]
+            
+            def process(self, world, components):
+                ...
+
+      If the system shall operate on combined component sets as specified
+      by the :class:`Applicator`, the class instance must contain a
+      ``is_applicator`` property, that evaluates to ``True`` ::
+      
+        class MyApplicator(object):
+            def __init__(self):
+                self.is_applicator = True
+                self.componenttypes = [...]
+            
+            def process(self, world, components):
+                pass
+      
+      The behaviour can be changed at run-time. The ``is_applicator`` attribute
+      is evaluated for every call to :meth:`World.process()`.
+      
+   .. method:: delete(entity : Entity)
+
+      Removes an :class:`Entity` from the World, including all its
+      component data.
+
+   .. method:: delete_entities(entities : iterable)
+
+      Removes a set of :class:`Entity` instances from the World,
+      including all their component data.
+
+   .. method:: insert_system(index : int, system : System)
+
+      Adds a processing :class:`System` to the world. The system will be
+      added at the specified position in the processing order.
+
+   .. method:: get_entities(component : object) -> [Entity, ...]
+
+      Gets the entities using the passed component.
+
+      .. note::
+
+         This will not perform an identity check on the component
+         but rely on its ``__eq__`` implementation instead.
+
+   .. method:: process()
+
+      Processes all component items within their corresponding
+      :class:`System` instances.
+
+   .. method:: remove_system(system : System)
+
+      Removes a processing :class:`System` from the world.

File doc/images/copprocessing.dia

Binary file added.

File doc/images/copprocessing.png

Added
New image

File doc/images/ebs.dia

Binary file added.

File doc/images/ebs.png

Added
New image

File doc/index.rst

+Welcome to the Python Utilities documentation!
+==============================================
+
+Contents
+========
+
+.. toctree::
+   :maxdepth: 2
+
+   array.rst
+   compat.rst
+   dll.rst
+   ebs.rst
+   resources.rst
+   sysfont.rst
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+
+Documentation TODOs
+===================
+
+.. todolist::
+
+Last generated on: |today|

File doc/make.bat

+@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\Priddle.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Priddle.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

File doc/python.inv

Binary file added.

File doc/resources.rst

+.. module:: resources
+   :synopsis: Resource management.
+
+resources - Resource management
+===============================
+Every application usually ships with various resources, such as image and data
+files, configuration files and so on. Accessing those files in the folder
+hierarchy or in a bundled format for various platforms can become a comple
+task, for which the :mod:`resources` module can provide ideal
+supportive application components.
+
+The :class:`Resources` class allows you to manage different application data
+in a certain directory, providing a dictionary-style access functionality for
+your in-application resources.
+
+Let's assume, your application has the following installation layout ::
+
+    Application Directory
+        Application.exe
+        Application.conf
+        data/
+            background.jpg
+            button1.jpg
+            button2.jpg
+            info.dat
+
+Within the ``Application.exe`` code, you can - completely system-agnostic -
+define a new resource that keeps track of all ``data`` items. ::
+
+    apppath = os.path.dirname(os.path.abspath(__file__))
+    appresources = Resources(os.path.join(apppath, "data"))
+    # Access some images
+    bgimage = appresources.get("background.jpg")
+    btn1image = appresources.get("button1.jpg")
+    ...
+
+To access individual files, you do not need to concat paths the whole
+time and regardless of the current directory, your application operates
+on, you can access your resource files at any time through the
+:class:`Resources` instance, you created initially.
+
+The :class:`Resources` class is also able to scan an index archived files,
+compressed via ZIP or TAR (gzip or bzip2 compression), and subdiectories
+automatically. ::
+
+    Application Directory
+        Application.exe
+        Application.conf
+        data/
+            audio/
+                example.wav
+            background.jpg
+            button1.jpg
+            button2.jpg
+            graphics.zip
+                [tileset1.bmp
+                 tileset2.bmp
+                 tileset3.bmp
+                 ]
+            info.dat
+
+    tilesimage = appresources.get("tileset1.bmp")
+    audiofile = appresources.get("example.wav")
+
+If you request an indexed file via :meth:`Resources.get`, you will receive
+a :class:`io.BytesIO` stream, containing the file data, for further processing.
+
+.. note::
+
+   The scanned files act as keys within the :class:`Resources` class. This
+   means that two files, that have the same name, but are located in different
+   directories, will not be indexed. Only one of them will be accessible
+   through the :class:`Resources` class.
+
+API
+---
+
+.. class:: Resources([path=None[, subdir=None[, excludepattern=None]]])
+
+   The Resources class manages a set of file resources and eases
+   accessing them by using relative paths, scanning archives
+   automatically and so on.
+
+   .. method:: add(filename : string)
+
+      Adds a file to the resource container. Depending on the
+      file type (determined by the file suffix or name) the file will be
+      automatically scanned (if it is an archive) or checked for
+      availability (if it is a stream or network resource).
+
+   .. method:: add_archive(filename : string[, typehint="zip"])
+
+      Adds an archive file to the resource container. This will scan the
+      passed archive and add its contents to the list of available and
+      accessible resources.
+
+   .. method:: add_file(filename : string)
+
+      Adds a file to the resource container. This will only add the
+      passed file and do not scan an archive or check the file for
+      availability.
+
+   .. method:: get(filename : string) -> BytesIO
+
+      Gets a specific file from the resource container.
+
+      Raises a :exc:`KeyError`, if the *filename* could not be found.
+
+   .. method:: get_filelike(filename : string) -> file object
+
+      Similar to :meth:`get()`, but tries to return the original file
+      handle, if possible. If the found file is only available within an
+      archive, a :class:`io.BytesIO` instance will be returned.
+
+      Raises a :exc:`KeyError`, if the *filename* could not be found.
+
+   .. method:: get_path(filename : string) -> string
+
+      Gets the path of the passed *filename*. If *filename* is only
+      available within an archive, a string in the form
+      ``filename@archivename`` will be returned.
+
+      Raises a :exc:`KeyError`, if the *filename* could not be found.
+
+   .. method:: scan(path : string[, subdir=None[, excludepattern=None])
+
+      Scans a path and adds all found files to the resource
+      container. If a file within the path is a supported archive (ZIP
+      or TAR), its contents will be indexed aut added automatically.
+
+      The method will consider the directory part (``os.path.dirname``)
+      of the provided *path* as path to scan, if the path is not a
+      directory. If *subdir* is provided, it will be appended to the
+      path and used as starting point for adding files to the resource
+      container.
+
+      *excludepattern* can be a regular expression to skip
+      directories, which match the pattern.
+
+.. function:: open_tarfile(archive : string, filename : string \
+                           [, directory=None[, ftype=None]]) -> BytesIO
+
+   Opens and reads a certain file from a TAR archive. The result is
+   returned as :class:`BytesIO` stream. *filename* can be a relative
+   or absolute path within the TAR archive. The optional *directory*
+   argument can be used to supply a relative directory path, under which
+   *filename* will be searched.
+
+   *ftype* is used to supply additional compression information, in
+   case the system cannot determine the compression type itself, and can
+   be either **"gz"** for gzip compression or **"bz2"** for bzip2
+   compression.
+
+   If the filename could not be found or an error occured on reading it,
+   ``None`` will be returned.
+
+   Raises a :exc:`TypeError`, if *archive* is not a valid TAR archive or
+   if *ftype* is not a valid value of ("gz", "bz2").
+
+   .. note::
+
+      If *ftype* is supplied, the compression mode will be enforced for
+      opening and reading.
+
+.. function:: open_url(filename : string[, basepath=None]) -> file object
+
+    Opens and reads a certain file from a web or remote location. This
+    function utilizes the :mod:`urllib2` module for Python 2.7 and
+    :mod:`urllib` for Python 3.x, which means that it is restricted to
+    the types of remote locations supported by the module.
+
+    *basepath* can be used to supply an additional location prefix.
+
+.. function:: open_zipfile(archive : string, filename : string \
+                           [, directory : string]) -> BytesIO
+
+   Opens and reads a certain file from a ZIP archive. The result is
+   returned as :class:`BytesIO` stream. *filename* can be a relative
+   or absolute path within the ZIP archive. The optional *directory*
+   argument can be used to supply a relative directory path, under which
+   *filename* will be searched.
+
+   If the filename could not be found, a :exc:`KeyError` will be raised.
+   Raises a :exc:`TypeError`, if *archive* is not a valid ZIP archive.

File doc/sysfont.rst

+.. module:: sysfont
+   :synopsis: Font dectection helpers
+
+sysfont - Font detection helpers
+================================
+The :mod:`sysfont` module enables you to find fonts installed on the
+underlying operating system. It supports Win32 and fontconfig-based
+(most Unix-like ones, such as Linux or BSD) systems.
+
+.. data:: STYLE_NORMAL
+
+   Indicates a normal font style.
+
+.. data:: STYLE_BOLD
+
+   Indicates a bold font style.
+
+.. data:: STYLE_ITALIC
+
+   Indicates an italic font style.
+
+.. function:: init() -> None
+
+   Initializes the internal font cache. This does not need to be called
+   explicitly. It is called automatically, if one of the retrieval functions
+   is executed for the first time.
+
+.. function:: get_font(name : string[, style=STYLE_NORMAL[, ftype=None]]) -> (str, str, int, str, str)
+
+   Retrieves the best matching font file for the given *name* and criteria.
+   The return value will be a, containing the following information:
+   *(family, font name, font style, font type, filename)*
+
+   * family: string, denotes the font family
+   * font name: string, the name of the font
+   * font style: int, a combination of the different ``STYLE_`` values
+   * font type: string, the font file type (e.g. TTF, OTF, ...)
+   * filename: the name of the physical file
+   
+   If no font could be found, ``None`` will be returned.
+   
+.. function:: get_fonts(name : string[, style=STYLE_NORMAL[, ftype=None]]) -> ((str, str, int, str, str), ...)
+
+   Retrieves all fonts matching the given family or font name, *style* and, if
+   provided, font file type. The return values will be tuples, containing the
+   following information: *(family, font name, font style, font type, filename)*
+
+   * family: string, denotes the font family
+   * font name: string, the name of the font
+   * font style: int, a combination of the different ``STYLE_`` values
+   * font type: string, the font file type (e.g. TTF, OTF, ...)
+   * filename: the name of the physical file
+   
+   If no font could be found, ``None`` will be returned.
+   
+.. function:: list_fonts() -> iterator
+
+   Retrieves an iterator over all found fonts. The values of the
+   iterator will be tuples, containing the following information:
+   *(family, font name, font style, font type, filename)*
+
+   * family: string, denotes the font family
+   * font name: string, the name of the font
+   * font style: int, a combination of the different ``STYLE_`` values
+   * font type: string, the font file type (e.g. TTF, OTF, ...)
+   * filename: the name of the physical file
+
+

File font.py

-"""OS-specific font detection."""
-import os
-import sys
-from subprocess import Popen, PIPE
-from compat import ISPYTHON2, stringify
-
-if sys.platform in ("win32", "cli"):
-    if ISPYTHON2:
-        import _winreg as winreg
-    else:
-        import winreg
-
-__all__ = ["STYLE_NORMAL", "STYLE_BOLD", "STYLE_ITALIC",
-           "init", "list_fonts", "get_fonts", "get_font"
-           ]
-
-# Font cache entries:
-# { family : [...,
-#             (name, styles, fonttype, filename)
-#             ...
-#            ]
-# }
-__FONTCACHE = None
-
-
-STYLE_NORMAL = 0x00
-STYLE_BOLD =   0x01
-STYLE_ITALIC = 0x02
-
-
-def _add_font(family, name, styles, fonttype, filename):
-    """Adds a font to the internal font cache."""
-    global __FONTCACHE
-
-    if family not in __FONTCACHE:
-        __FONTCACHE[family] = []
-    __FONTCACHE[family].append((name, styles, fonttype, filename))
-
-
-def _cache_fonts_win32():
-    """Caches fonts on a Win32 platform."""
-    key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
-    regfonts = []
-    try:
-        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key) as fontkey:
-            idx = 0
-            enumval = winreg.EnumValue
-            rappend = regfonts.append
-            while True:
-                rappend(enumval(fontkey, idx)[:2])
-                idx += 1
-    except WindowsError:
-        pass
-
-    # TODO: integrate alias handling for fonts within the registry.
-    # TODO: Scan and index fonts from %SystemRoot%\\Fonts that are not in the
-    # registry
-
-    # Received all fonts from the registry.
-    for name, filename in regfonts:
-        fonttype = os.path.splitext(filename)[1][1:].lower()
-        if name.endswith("(TrueType)"):
-            name = name[:-10].strip()
-        if name.endswith("(All Res)"):
-            name = name[:-9].strip()
-        style = STYLE_NORMAL
-        if name.find(" Bold") >= 0:
-            style |= STYLE_BOLD
-        if name.find(" Italic") >= 0 or name.find(" Oblique") >= 0:
-            style |= STYLE_ITALIC
-
-        family = name
-        for rm in ("Bold", "Italic", "Oblique"):
-            family = family.replace(rm, "")
-        family = family.lower().strip()
-
-        fontpath = os.environ.get("SystemRoot", "C:\\Windows")
-        fontpath = os.path.join(fontpath, "Fonts")
-        if filename.find("\\") == -1:
-            # No path delimiter is given; we assume it to be a font in
-            # %SystemRoot%\Fonts
-            filename = os.path.join(fontpath, filename)
-        _add_font(family, name, style, fonttype, filename)
-
-
-def _cache_fonts_darwin():
-    """Caches fonts on Mac OS."""
-    raise NotImplementedError("Mac OS X support is not given yet")
-
-
-def _cache_fonts_fontconfig():
-    """Caches font on POSIX-alike platforms."""
-    try:
-        command = "fc-list : file family style fullname fullnamelang"
-        proc = Popen(command, stdout=PIPE, shell=True, stderr=PIPE)
-        pout = proc.communicate()[0]
-        output = stringify(pout, "utf-8")
-    except OSError:
-        return
-
-    for entry in output.split(os.linesep):
-        if entry.strip() == "":
-            continue
-        values = entry.split(":")
-        filename = values[0]
-
-        # get the font type
-        fname, fonttype = os.path.splitext(filename)
-        if fonttype == ".gz":
-            fonttype = os.path.splitext(fname)[1][1:].lower()
-        else:
-            fonttype = fonttype.lstrip(".").lower()
-
-        # get the font name
-        name = None
-        if len(values) > 3:
-            fullnames, fullnamelangs = values[3:]
-            langs = fullnamelangs.split(",")
-            offset = langs.index("fullnamelang=en")
-            if offset == -1:
-                offset = langs.index("en")
-            if offset != -1:
-                # got an english name, use that one
-                name = fullnames.split(",")[offset]
-                if name.startswith("fullname="):
-                    name = name[9:]
-        if name is None:
-            if fname.endswith(".pcf") or fname.endswith(".bdf"):
-                name = os.path.basename(fname[:-4])
-            else:
-                name = os.path.basename(fname)
-        name = name.lower()
-
-        # family and styles
-        family = values[1].strip().lower()
-        stylevals = values[2].strip()
-        style = STYLE_NORMAL
-
-        if stylevals.find("Bold") >= 0:
-            style |= STYLE_BOLD
-        if stylevals.find("Italic") >= 0 or stylevals.find("Oblique") >= 0:
-            style |= STYLE_ITALIC
-        _add_font(family, name, style, fonttype, filename)
-
-
-def init():
-    """Initialises the internal font cache.
-
-    It does not need to be called explicitly.
-    """
-    global __FONTCACHE
-    if __FONTCACHE is not None:
-        return
-    __FONTCACHE = {}
-    if sys.platform in ("win32", "cli"):
-        _cache_fonts_win32()
-    elif sys.platform == "darwin":
-        _cache_fonts_darwin()
-    else:
-        _cache_fonts_fontconfig()
-
-
-def list_fonts():
-    """Returns an iterator over the cached fonts."""
-    if __FONTCACHE is None:
-        init()
-    if len(__FONTCACHE) == 0:
-        yield None
-    for family, entries in __FONTCACHE.items():
-        for fname, styles, fonttype, filename in entries:
-            yield (family, fname, styles, fonttype, filename)
-
-
-def get_fonts(name, style=STYLE_NORMAL, ftype=None):
-    """Retrieves all fonts matching the given family or font name."""
-    if __FONTCACHE is None:
-        init()
-    if len(__FONTCACHE) == 0:
-        return None
-
-    results = []
-    rappend = results.append
-
-    name = name.lower()
-    if ftype:
-        ftype = ftype.lower()
-
-    fonts = __FONTCACHE.get(name, [])
-    for fname, fstyles, fonttype, filename in fonts:
-        if ftype and fonttype != ftype:
-            # ignore font filetype mismatches
-            continue
-        if (fstyles & style) == style:
-            rappend(filename)
-
-    for family, fonts in __FONTCACHE.items():
-        for fname, fstyles, fonttype, filename in fonts:
-            if fname.lower() == name and filename not in results:
-                rappend(filename)
-    return results
-
-
-def get_font(name, style=STYLE_NORMAL, ftype=None):
-    """Retrieves the best matching font file for the given name and
-    criteria.
-    """
-    retvals = get_fonts(name, style, ftype)
-    if len(retvals) > 0:
-        return retvals[0]
-    return None
+"""OS-specific font detection."""
+import os
+import sys
+from subprocess import Popen, PIPE
+from compat import ISPYTHON2, stringify
+
+if sys.platform in ("win32", "cli"):
+    if ISPYTHON2:
+        import _winreg as winreg
+    else:
+        import winreg
+
+__all__ = ["STYLE_NORMAL", "STYLE_BOLD", "STYLE_ITALIC",
+           "init", "list_fonts", "get_fonts", "get_font"
+           ]
+
+# Font cache entries:
+# { family : [...,
+#             (name, styles, fonttype, filename)
+#             ...
+#            ]
+# }
+__FONTCACHE = None
+
+
+STYLE_NORMAL = 0x00
+STYLE_BOLD =   0x01
+STYLE_ITALIC = 0x02
+
+
+def _add_font(family, name, styles, fonttype, filename):
+    """Adds a font to the internal font cache."""
+    global __FONTCACHE
+
+    if family not in __FONTCACHE:
+        __FONTCACHE[family] = []
+    __FONTCACHE[family].append((name, styles, fonttype, filename))
+
+
+def _cache_fonts_win32():
+    """Caches fonts on a Win32 platform."""
+    key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
+    regfonts = []
+    try:
+        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key) as fontkey:
+            idx = 0
+            enumval = winreg.EnumValue
+            rappend = regfonts.append
+            while True:
+                rappend(enumval(fontkey, idx)[:2])
+                idx += 1
+    except WindowsError:
+        pass
+
+    # TODO: integrate alias handling for fonts within the registry.
+    # TODO: Scan and index fonts from %SystemRoot%\\Fonts that are not in the
+    # registry
+
+    # Received all fonts from the registry.
+    for name, filename in regfonts:
+        fonttype = os.path.splitext(filename)[1][1:].lower()
+        if name.endswith("(TrueType)"):
+            name = name[:-10].strip()
+        if name.endswith("(All Res)"):
+            name = name[:-9].strip()
+        style = STYLE_NORMAL
+        if name.find(" Bold") >= 0:
+            style |= STYLE_BOLD
+        if name.find(" Italic") >= 0 or name.find(" Oblique") >= 0:
+            style |= STYLE_ITALIC
+
+        family = name
+        for rm in ("Bold", "Italic", "Oblique"):
+            family = family.replace(rm, "")
+        family = family.lower().strip()
+
+        fontpath = os.environ.get("SystemRoot", "C:\\Windows")
+        fontpath = os.path.join(fontpath, "Fonts")
+        if filename.find("\\") == -1:
+            # No path delimiter is given; we assume it to be a font in
+            # %SystemRoot%\Fonts
+            filename = os.path.join(fontpath, filename)
+        _add_font(family, name, style, fonttype, filename)
+
+
+def _cache_fonts_darwin():
+    """Caches fonts on Mac OS."""
+    raise NotImplementedError("Mac OS X support is not given yet")
+
+
+def _cache_fonts_fontconfig():
+    """Caches font on POSIX-alike platforms."""
+    try:
+        command = "fc-list : file family style fullname fullnamelang"
+        proc = Popen(command, stdout=PIPE, shell=True, stderr=PIPE)
+        pout = proc.communicate()[0]
+        output = stringify(pout, "utf-8")
+    except OSError:
+        return
+
+    for entry in output.split(os.linesep):
+        if entry.strip() == "":
+            continue
+        values = entry.split(":")
+        filename = values[0]
+
+        # get the font type
+        fname, fonttype = os.path.splitext(filename)
+        if fonttype == ".gz":
+            fonttype = os.path.splitext(fname)[1][1:].lower()
+        else:
+            fonttype = fonttype.lstrip(".").lower()
+
+        # get the font name
+        name = None
+        if len(values) > 3:
+            fullnames, fullnamelangs = values[3:]
+            langs = fullnamelangs.split(",")
+            offset = langs.index("fullnamelang=en")
+            if offset == -1:
+                offset = langs.index("en")
+            if offset != -1:
+                # got an english name, use that one
+                name = fullnames.split(",")[offset]
+                if name.startswith("fullname="):
+                    name = name[9:]
+        if name is None:
+            if fname.endswith(".pcf") or fname.endswith(".bdf"):
+                name = os.path.basename(fname[:-4])
+            else:
+                name = os.path.basename(fname)
+        name = name.lower()
+
+        # family and styles
+        family = values[1].strip().lower()
+        stylevals = values[2].strip()
+        style = STYLE_NORMAL
+
+        if stylevals.find("Bold") >= 0:
+            style |= STYLE_BOLD
+        if stylevals.find("Italic") >= 0 or stylevals.find("Oblique") >= 0:
+            style |= STYLE_ITALIC
+        _add_font(family, name, style, fonttype, filename)
+
+
+def init():
+    """Initialises the internal font cache.
+
+    It does not need to be called explicitly.
+    """
+    global __FONTCACHE
+    if __FONTCACHE is not None:
+        return
+    __FONTCACHE = {}
+    if sys.platform in ("win32", "cli"):
+        _cache_fonts_win32()
+    elif sys.platform == "darwin":
+        _cache_fonts_darwin()
+    else:
+        _cache_fonts_fontconfig()
+
+
+def list_fonts():
+    """Returns an iterator over the cached fonts."""
+    if __FONTCACHE is None:
+        init()
+    if len(__FONTCACHE) == 0:
+        yield None
+    for family, entries in __FONTCACHE.items():
+        for fname, styles, fonttype, filename in entries:
+            yield (family, fname, styles, fonttype, filename)
+
+
+def get_fonts(name, style=STYLE_NORMAL, ftype=None):
+    """Retrieves all fonts matching the given family or font name."""
+    if __FONTCACHE is None:
+        init()
+    if len(__FONTCACHE) == 0:
+        return None
+
+    results = []
+    rappend = results.append
+
+    name = name.lower()
+    if ftype:
+        ftype = ftype.lower()
+
+    fonts = __FONTCACHE.get(name, [])
+    for fname, fstyles, fonttype, filename in fonts:
+        if ftype and fonttype != ftype:
+            # ignore font filetype mismatches
+            continue
+        if (fstyles & style) == style:
+            rappend(filename)
+
+    for family, fonts in __FONTCACHE.items():
+        for fname, fstyles, fonttype, filename in fonts:
+            if fname.lower() == name and filename not in results:
+                rappend(filename)
+    return results
+
+
+def get_font(name, style=STYLE_NORMAL, ftype=None):
+    """Retrieves the best matching font file for the given name and
+    criteria.
+    """
+    retvals = get_fonts(name, style, ftype)
+    if len(retvals) > 0:
+        return retvals[0]
+    return None