mirror of https://github.com/McSinyx/palace
Add design principles
This commit is contained in:
parent
9265cec923
commit
cb2601c7f5
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,185 @@
|
|||
Design Principles
|
||||
=================
|
||||
|
||||
In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API alure_.
|
||||
|
||||
This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.
|
||||
|
||||
.. _alure: https://github.com/kcat/alure
|
||||
|
||||
.. _impl-idiom:
|
||||
|
||||
The Impl Idiom
|
||||
--------------
|
||||
|
||||
*Not to be confused with* `the pimpl idiom`_.
|
||||
|
||||
For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef extern from 'foobar.h' namespace 'foobar':
|
||||
cdef cppclass Foo:
|
||||
Foo()
|
||||
float meth(size_t crack) except +
|
||||
...
|
||||
|
||||
The Cython extension type can then be declared as follows
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Bar:
|
||||
cdef Foo impl
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.impl = ...
|
||||
|
||||
@staticmethod
|
||||
def from_baz(baz: Baz) -> Bar:
|
||||
bar = Bar.__new__(Bar)
|
||||
bar.impl = ...
|
||||
return bar
|
||||
|
||||
def meth(self, crack: int) -> float:
|
||||
return self.impl.meth(crack)
|
||||
|
||||
.. _`the pimpl idiom`: https://wiki.c2.com/?PimplIdiom
|
||||
|
||||
The Modern Python
|
||||
-----------------
|
||||
|
||||
One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.
|
||||
|
||||
.. _getter-setter:
|
||||
|
||||
Property Attributes
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use property_ to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.
|
||||
|
||||
Due to implementation details, Cython has to hijack the ``@property`` decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to ``property`` as follows
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
getter = property
|
||||
setter = lambda fset: property(fset=fset, doc=fset.__doc__)
|
||||
|
||||
Then ``@getter`` and ``@setter`` can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.
|
||||
|
||||
.. _property: https://docs.python.org/3/library/functions.html#property
|
||||
|
||||
Context Managers
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote *The Zen of Python*,
|
||||
|
||||
| If the implementation is hard to explain, it's a bad idea.
|
||||
| If the implementation is easy to explain, it may be a good idea.
|
||||
|
||||
With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Device:
|
||||
cdef alure.Device impl
|
||||
|
||||
def __init__(self, name: str = '') -> None:
|
||||
self.impl = devmgr.open_playback(name)
|
||||
|
||||
def __enter__(self) -> Device:
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> Optional[bool]:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
self.impl.close()
|
||||
|
||||
Now if the ``with`` statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with Device() as dev:
|
||||
...
|
||||
|
||||
as it is equivalent to
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dev = Device()
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
dev.close()
|
||||
|
||||
Other than closure/destruction of objects, typical uses of `context managers`__
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
:py:class:`palace.Context`), locking and unlocking resources, etc.
|
||||
|
||||
__ https://docs.python.org/3/reference/datamodel.html#context-managers
|
||||
|
||||
The Double Reference
|
||||
--------------------
|
||||
|
||||
While wrapping C++ interfaces, :ref:`the impl idiom <impl-idiom>` might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we'll need to handle the reference count ourselves, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef cppclass CppDecoder(alure.BaseDecoder):
|
||||
Decoder pyo
|
||||
|
||||
__init__(Decoder decoder):
|
||||
this.pyo = decoder
|
||||
Py_INCREF(pyo)
|
||||
|
||||
__dealloc__():
|
||||
Py_DECREF(pyo)
|
||||
|
||||
bool seek(uint64_t pos):
|
||||
return pyo.seek(pos)
|
||||
|
||||
With this being done, we can now write the wrapper as simply as
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class BaseDecoder:
|
||||
cdef shared_ptr[alure.Decoder] pimpl
|
||||
|
||||
def __cinit__(self, *args, **kwargs) -> None:
|
||||
self.pimpl = shared_ptr[alure.Decoder](new CppDecoder(self))
|
||||
|
||||
def seek(pos: int) -> bool:
|
||||
...
|
||||
|
||||
Because ``__cinit__`` is called by ``__new__``, any Python class derived
|
||||
from ``BaseDecoder`` will be exposed to C++ as an attribute of ``CppDecoder``.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.
|
||||
|
||||
In practice, :py:class:`palace.BaseDecoder` will also need to take into account
|
||||
other guarding mechanisms like :py:class:`abc.ABC`. Due to Cython limitations,
|
||||
implementation as a pure Python class and :ref:`aliasing <getter-setter>` of
|
||||
``@getter``/``@setter`` should be considered.
|
|
@ -1,16 +1,12 @@
|
|||
.. palace documentation master file, created by
|
||||
sphinx-quickstart on Sun Jan 5 19:56:04 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to palace's documentation!
|
||||
==================================
|
||||
Welcome to our palace!
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
installation
|
||||
design
|
||||
reference
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
.. automodule:: palace
|
||||
:members:
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Design Principles — palace 0.0.12 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Reference" href="reference.html" />
|
||||
<link rel="prev" title="Installation" href="installation.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
|
||||
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="design-principles">
|
||||
<h1>Design Principles<a class="headerlink" href="#design-principles" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API <a class="reference external" href="https://github.com/kcat/alure">alure</a>.</p>
|
||||
<p>This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.</p>
|
||||
<div class="section" id="the-impl-idiom">
|
||||
<span id="impl-idiom"></span><h2>The Impl Idiom<a class="headerlink" href="#the-impl-idiom" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>Not to be confused with</em> <a class="reference external" href="https://wiki.c2.com/?PimplIdiom">the pimpl idiom</a>.</p>
|
||||
<p>For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kr">extern</span> <span class="k">from</span> <span class="s">'foobar.h'</span> <span class="n">namespace</span> <span class="s">'foobar'</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">Foo</span><span class="p">:</span>
|
||||
<span class="n">Foo</span><span class="p">()</span>
|
||||
<span class="nb">float</span> <span class="n">meth</span><span class="p">(</span><span class="n">size_t</span> <span class="n">crack</span><span class="p">)</span> <span class="k">except</span> <span class="o">+</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Cython extension type can then be declared as follows</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Bar</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">Foo</span> <span class="nf">impl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||||
|
||||
<span class="nd">@staticmethod</span>
|
||||
<span class="k">def</span> <span class="nf">from_baz</span><span class="p">(</span><span class="n">baz</span><span class="p">:</span> <span class="n">Baz</span><span class="p">)</span> <span class="o">-></span> <span class="n">Bar</span><span class="p">:</span>
|
||||
<span class="n">bar</span> <span class="o">=</span> <span class="n">Bar</span><span class="o">.</span><span class="n">__new__</span><span class="p">(</span><span class="n">Bar</span><span class="p">)</span>
|
||||
<span class="n">bar</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||||
<span class="k">return</span> <span class="n">bar</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crack</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">meth</span><span class="p">(</span><span class="n">crack</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-modern-python">
|
||||
<h2>The Modern Python<a class="headerlink" href="#the-modern-python" title="Permalink to this headline">¶</a></h2>
|
||||
<p>One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.</p>
|
||||
<div class="section" id="property-attributes">
|
||||
<span id="getter-setter"></span><h3>Property Attributes<a class="headerlink" href="#property-attributes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use <a class="reference external" href="https://docs.python.org/3/library/functions.html#property">property</a> to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.</p>
|
||||
<p>Due to implementation details, Cython has to hijack the <code class="docutils literal notranslate"><span class="pre">@property</span></code> decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to <code class="docutils literal notranslate"><span class="pre">property</span></code> as follows</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">getter</span> <span class="o">=</span> <span class="nb">property</span>
|
||||
<span class="n">setter</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">fset</span><span class="p">:</span> <span class="nb">property</span><span class="p">(</span><span class="n">fset</span><span class="o">=</span><span class="n">fset</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="n">fset</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Then <code class="docutils literal notranslate"><span class="pre">@getter</span></code> and <code class="docutils literal notranslate"><span class="pre">@setter</span></code> can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.</p>
|
||||
</div>
|
||||
<div class="section" id="context-managers">
|
||||
<h3>Context Managers<a class="headerlink" href="#context-managers" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote <em>The Zen of Python</em>,</p>
|
||||
<blockquote>
|
||||
<div><div class="line-block">
|
||||
<div class="line">If the implementation is hard to explain, it’s a bad idea.</div>
|
||||
<div class="line">If the implementation is easy to explain, it may be a good idea.</div>
|
||||
</div>
|
||||
</div></blockquote>
|
||||
<p>With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Device</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">alure</span>.<span class="kt">Device</span> <span class="nf">impl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">''</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="n">devmgr</span><span class="o">.</span><span class="n">open_playback</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Device</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">exc</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now if the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">Device</span><span class="p">()</span> <span class="k">as</span> <span class="n">dev</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>as it is equivalent to</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">dev</span> <span class="o">=</span> <span class="n">Device</span><span class="p">()</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
<span class="k">finally</span><span class="p">:</span>
|
||||
<span class="n">dev</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Other than closure/destruction of objects, typical uses of <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#context-managers">context managers</a>
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
<a class="reference internal" href="reference.html#palace.Context" title="palace.Context"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Context</span></code></a>), locking and unlocking resources, etc.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-double-reference">
|
||||
<h2>The Double Reference<a class="headerlink" href="#the-double-reference" title="Permalink to this headline">¶</a></h2>
|
||||
<p>While wrapping C++ interfaces, <a class="reference internal" href="#impl-idiom"><span class="std std-ref">the impl idiom</span></a> might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we’ll need to handle the reference count ourselves, e.g.</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">CppDecoder</span><span class="p">(</span><span class="n">alure</span><span class="o">.</span><span class="n">BaseDecoder</span><span class="p">):</span>
|
||||
<span class="n">Decoder</span> <span class="n">pyo</span>
|
||||
|
||||
<span class="n">__init__</span><span class="p">(</span><span class="n">Decoder</span> <span class="n">decoder</span><span class="p">):</span>
|
||||
<span class="n">this</span><span class="o">.</span><span class="n">pyo</span> <span class="o">=</span> <span class="n">decoder</span>
|
||||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||||
|
||||
<span class="n">__dealloc__</span><span class="p">():</span>
|
||||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||||
|
||||
<span class="nb">bool</span> <span class="n">seek</span><span class="p">(</span><span class="n">uint64_t</span> <span class="n">pos</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">pyo</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>With this being done, we can now write the wrapper as simply as</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">BaseDecoder</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">shared_ptr</span>[<span class="kt">alure</span>.<span class="kt">Decoder</span>] <span class="nf">pimpl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__cinit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">pimpl</span> <span class="o">=</span> <span class="n">shared_ptr</span><span class="p">[</span><span class="n">alure</span><span class="o">.</span><span class="n">Decoder</span><span class="p">](</span><span class="n">new</span> <span class="n">CppDecoder</span><span class="p">(</span><span class="bp">self</span><span class="p">))</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Because <code class="docutils literal notranslate"><span class="pre">__cinit__</span></code> is called by <code class="docutils literal notranslate"><span class="pre">__new__</span></code>, any Python class derived
|
||||
from <code class="docutils literal notranslate"><span class="pre">BaseDecoder</span></code> will be exposed to C++ as an attribute of <code class="docutils literal notranslate"><span class="pre">CppDecoder</span></code>.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.</p>
|
||||
<p>In practice, <a class="reference internal" href="reference.html#palace.BaseDecoder" title="palace.BaseDecoder"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.BaseDecoder</span></code></a> will also need to take into account
|
||||
other guarding mechanisms like <code class="xref py py-class docutils literal notranslate"><span class="pre">abc.ABC</span></code>. Due to Cython limitations,
|
||||
implementation as a pure Python class and <a class="reference internal" href="#getter-setter"><span class="std std-ref">aliasing</span></a> of
|
||||
<code class="docutils literal notranslate"><span class="pre">@getter</span></code>/<code class="docutils literal notranslate"><span class="pre">@setter</span></code> should be considered.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h1 class="logo"><a href="index.html">palace</a></h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Navigation</h3>
|
||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Design Principles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-impl-idiom">The Impl Idiom</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-modern-python">The Modern Python</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-double-reference">The Double Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="installation.html" title="previous chapter">Installation</a></li>
|
||||
<li>Next: <a href="reference.html" title="next chapter">Reference</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
<a href="_sources/design.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -557,6 +557,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Welcome to palace’s documentation! — palace 0.0.12 documentation</title>
|
||||
<title>Welcome to our palace! — palace 0.0.12 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
|
@ -31,8 +31,8 @@
|
|||
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="welcome-to-palace-s-documentation">
|
||||
<h1>Welcome to palace’s documentation!<a class="headerlink" href="#welcome-to-palace-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="welcome-to-our-palace">
|
||||
<h1>Welcome to our palace!<a class="headerlink" href="#welcome-to-our-palace" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="toctree-wrapper compound">
|
||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
|
@ -42,9 +42,13 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="installation.html#from-source">From source</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a><ul class="simple">
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-impl-idiom">The Impl Idiom</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-modern-python">The Modern Python</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-double-reference">The Double Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,6 +81,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
|
BIN
html/objects.inv
BIN
html/objects.inv
Binary file not shown.
|
@ -75,6 +75,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<script src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="Installation" href="installation.html" />
|
||||
<link rel="prev" title="Design Principles" href="design.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
@ -31,11 +31,9 @@
|
|||
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="reference">
|
||||
<h1>Reference<a class="headerlink" href="#reference" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="toctree-wrapper compound">
|
||||
</div>
|
||||
<span class="target" id="module-palace"></span><p>Pythonic Audio Library and Codecs Environment</p>
|
||||
<div class="section" id="module-palace">
|
||||
<span id="reference"></span><h1>Reference<a class="headerlink" href="#module-palace" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Pythonic Audio Library and Codecs Environment</p>
|
||||
<dl class="attribute">
|
||||
<dt id="palace.Vector3">
|
||||
<code class="sig-prename descclassname">palace.</code><code class="sig-name descname">Vector3</code><a class="headerlink" href="#palace.Vector3" title="Permalink to this definition">¶</a></dt>
|
||||
|
@ -1637,16 +1635,15 @@ or stream, which is detected upon a call to <cite>Context.update</cite>.</p>
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Reference</a><ul class="simple">
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="installation.html" title="previous chapter">Installation</a></li>
|
||||
<li>Previous: <a href="design.html" title="previous chapter">Design Principles</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,185 @@
|
|||
Design Principles
|
||||
=================
|
||||
|
||||
In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API alure_.
|
||||
|
||||
This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.
|
||||
|
||||
.. _alure: https://github.com/kcat/alure
|
||||
|
||||
.. _impl-idiom:
|
||||
|
||||
The Impl Idiom
|
||||
--------------
|
||||
|
||||
*Not to be confused with* `the pimpl idiom`_.
|
||||
|
||||
For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef extern from 'foobar.h' namespace 'foobar':
|
||||
cdef cppclass Foo:
|
||||
Foo()
|
||||
float meth(size_t crack) except +
|
||||
...
|
||||
|
||||
The Cython extension type can then be declared as follows
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Bar:
|
||||
cdef Foo impl
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.impl = ...
|
||||
|
||||
@staticmethod
|
||||
def from_baz(baz: Baz) -> Bar:
|
||||
bar = Bar.__new__(Bar)
|
||||
bar.impl = ...
|
||||
return bar
|
||||
|
||||
def meth(self, crack: int) -> float:
|
||||
return self.impl.meth(crack)
|
||||
|
||||
.. _`the pimpl idiom`: https://wiki.c2.com/?PimplIdiom
|
||||
|
||||
The Modern Python
|
||||
-----------------
|
||||
|
||||
One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.
|
||||
|
||||
.. _getter-setter:
|
||||
|
||||
Property Attributes
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use property_ to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.
|
||||
|
||||
Due to implementation details, Cython has to hijack the ``@property`` decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to ``property`` as follows
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
getter = property
|
||||
setter = lambda fset: property(fset=fset, doc=fset.__doc__)
|
||||
|
||||
Then ``@getter`` and ``@setter`` can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.
|
||||
|
||||
.. _property: https://docs.python.org/3/library/functions.html#property
|
||||
|
||||
Context Managers
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote *The Zen of Python*,
|
||||
|
||||
| If the implementation is hard to explain, it's a bad idea.
|
||||
| If the implementation is easy to explain, it may be a good idea.
|
||||
|
||||
With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Device:
|
||||
cdef alure.Device impl
|
||||
|
||||
def __init__(self, name: str = '') -> None:
|
||||
self.impl = devmgr.open_playback(name)
|
||||
|
||||
def __enter__(self) -> Device:
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> Optional[bool]:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
self.impl.close()
|
||||
|
||||
Now if the ``with`` statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with Device() as dev:
|
||||
...
|
||||
|
||||
as it is equivalent to
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dev = Device()
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
dev.close()
|
||||
|
||||
Other than closure/destruction of objects, typical uses of `context managers`__
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
:py:class:`palace.Context`), locking and unlocking resources, etc.
|
||||
|
||||
__ https://docs.python.org/3/reference/datamodel.html#context-managers
|
||||
|
||||
The Double Reference
|
||||
--------------------
|
||||
|
||||
While wrapping C++ interfaces, :ref:`the impl idiom <impl-idiom>` might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we'll need to handle the reference count ourselves, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef cppclass CppDecoder(alure.BaseDecoder):
|
||||
Decoder pyo
|
||||
|
||||
__init__(Decoder decoder):
|
||||
this.pyo = decoder
|
||||
Py_INCREF(pyo)
|
||||
|
||||
__dealloc__():
|
||||
Py_DECREF(pyo)
|
||||
|
||||
bool seek(uint64_t pos):
|
||||
return pyo.seek(pos)
|
||||
|
||||
With this being done, we can now write the wrapper as simply as
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class BaseDecoder:
|
||||
cdef shared_ptr[alure.Decoder] pimpl
|
||||
|
||||
def __cinit__(self, *args, **kwargs) -> None:
|
||||
self.pimpl = shared_ptr[alure.Decoder](new CppDecoder(self))
|
||||
|
||||
def seek(pos: int) -> bool:
|
||||
...
|
||||
|
||||
Because ``__cinit__`` is called by ``__new__``, any Python class derived
|
||||
from ``BaseDecoder`` will be exposed to C++ as an attribute of ``CppDecoder``.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.
|
||||
|
||||
In practice, :py:class:`palace.BaseDecoder` will also need to take into account
|
||||
other guarding mechanisms like :py:class:`abc.ABC`. Due to Cython limitations,
|
||||
implementation as a pure Python class and :ref:`aliasing <getter-setter>` of
|
||||
``@getter``/``@setter`` should be considered.
|
|
@ -1,16 +1,12 @@
|
|||
.. palace documentation master file, created by
|
||||
sphinx-quickstart on Sun Jan 5 19:56:04 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to palace's documentation!
|
||||
==================================
|
||||
Welcome to our palace!
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
installation
|
||||
design
|
||||
reference
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
.. automodule:: palace
|
||||
:members:
|
||||
|
|
Loading…
Reference in New Issue