mirror of
https://github.com/capnproto/pycapnp.git
synced 2025-03-04 08:24:43 +01:00
Updating docs for v1.0.0b2
This commit is contained in:
parent
db4c6d5665
commit
38b1b37da0
28 changed files with 17887 additions and 2411 deletions
1261
_modules/importlib/_bootstrap.html
Normal file
1261
_modules/importlib/_bootstrap.html
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,32 +1,22 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Overview: module code — capnp 0.5.4 documentation</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Overview: module code — capnp 1.0.0b2 documentation</title>
|
||||
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '../',
|
||||
VERSION: '0.5.4',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="../_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="../_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||||
<link rel="top" title="capnp 0.5.4 documentation" href="../index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<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" />
|
||||
</head><body>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -35,42 +25,49 @@
|
|||
<li class="right" >
|
||||
<a href="../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="../index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="../index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Overview: module code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
<div class="body" role="main">
|
||||
|
||||
<h1>All modules for which code is available</h1>
|
||||
<ul><li><a href="capnp.html">capnp</a></li>
|
||||
<ul><li><a href="capnp/lib/capnp.html">capnp.lib.capnp</a></li>
|
||||
<li><a href="importlib/_bootstrap.html">importlib._bootstrap</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<h3><a href="../index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../install.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">Quickstart</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="../capnp.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<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" />
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -79,12 +76,13 @@
|
|||
<li class="right" >
|
||||
<a href="../py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="../index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="../index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Overview: module code</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Author.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander).
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.1.0.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
198
_sources/capnp.rst.txt
Normal file
198
_sources/capnp.rst.txt
Normal file
|
@ -0,0 +1,198 @@
|
|||
.. _api:
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. automodule:: capnp
|
||||
|
||||
.. currentmodule:: capnp
|
||||
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
RPC
|
||||
~~~
|
||||
|
||||
Promise
|
||||
#######
|
||||
|
||||
.. autoclass:: Promise
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Promise may be one of:
|
||||
|
||||
* :meth:`capnp.lib.capnp._Promise`
|
||||
* :meth:`capnp.lib.capnp._RemotePromise`
|
||||
* :meth:`capnp.lib.capnp._VoidPromise`
|
||||
* :meth:`PromiseFulfillerPair`
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._Promise
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._RemotePromise
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._VoidPromise
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: PromiseFulfillerPair
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Communication
|
||||
#############
|
||||
|
||||
.. autoclass:: TwoPartyClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: TwoPartyServer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Capability
|
||||
##########
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._DynamicCapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Response
|
||||
########
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._Response
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
.. autoclass:: KjException
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: SchemaParser
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
.. autofunction:: add_import_hook
|
||||
.. autofunction:: cleanup_global_schema_parser
|
||||
.. autofunction:: create_event_loop
|
||||
.. autofunction:: getTimer
|
||||
.. autofunction:: join_promises
|
||||
.. autofunction:: load
|
||||
.. autofunction:: poll_once
|
||||
.. autofunction:: remove_event_loop
|
||||
.. autofunction:: remove_import_hook
|
||||
.. autofunction:: reset_event_loop
|
||||
.. autofunction:: wait_forever
|
||||
|
||||
|
||||
Internal Classes
|
||||
----------------
|
||||
These classes are internal to the library. You will never need to allocate
|
||||
one yourself, but you may end up using some of their member methods.
|
||||
|
||||
Modules
|
||||
~~~~~~~
|
||||
These are classes that are made for you when you import a Cap'n Proto file::
|
||||
|
||||
import capnp
|
||||
import addressbook_capnp
|
||||
|
||||
print type(addressbook_capnp.Person) # capnp.capnp._StructModule
|
||||
|
||||
.. autoclass:: _InterfaceModule
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _StructModule
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Readers
|
||||
~~~~~~~
|
||||
.. autoclass:: _DynamicListReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicStructReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _PackedFdMessageReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _StreamFdMessageReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Builders
|
||||
~~~~~~~~
|
||||
.. autoclass:: _DynamicResizableListBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicListBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicStructBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _MallocMessageBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
RPC
|
||||
~~~
|
||||
.. autoclass:: _CapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicCapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
.. autoclass:: _DynamicOrphan
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
19
_sources/index.rst.txt
Normal file
19
_sources/index.rst.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
.. capnp documentation master file
|
||||
|
||||
pycapnp
|
||||
=======
|
||||
|
||||
This is a python wrapping of the C++ implementation of the `Cap'n Proto <https://capnproto.org/>`_ library. Here is a short description, quoted from its docs:
|
||||
|
||||
Cap’n Proto is an insanely fast data interchange format and capability-based RPC system. Think JSON, except binary. Or think Protocol Buffers, except faster. In fact, in benchmarks, Cap’n Proto is INFINITY TIMES faster than Protocol Buffers.
|
||||
|
||||
Since the python library is just a thin wrapping of the C++ library, we inherit a lot of what makes Cap'n Proto fast. In some simplistic benchmarks (available in the `benchmark directory of the repo <https://github.com/capnproto/pycapnp/tree/master/benchmark>`_), pycapnp has proven to be decently faster than Protocol Buffers (both pure python and C++ implementations). Also, the python capnp library can load Cap'n Proto schema files directly, without the need for a seperate compile step like with Protocol Buffers or Thrift. pycapnp is available on `github <https://github.com/capnproto/pycapnp.git>`_ and `pypi <https://pypi.python.org/pypi/pycapnp>`_.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
install
|
||||
quickstart
|
||||
capnp
|
97
_sources/install.rst.txt
Normal file
97
_sources/install.rst.txt
Normal file
|
@ -0,0 +1,97 @@
|
|||
.. _install:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Pip
|
||||
---
|
||||
The pip installation will using the binary versions of the package (if possible). These contain a bundled version of capnproto (on Linux compiled with `manylinux <https://github.com/pypa/manylinux>`_). Starting from v1.0.0b1 binary releases are available for Windows, macOS and Linux from `pypi <https://pypi.org/project/pycapnp/#history>`_::
|
||||
|
||||
[sudo] pip install pycapnp
|
||||
|
||||
To force rebuilding the pip package from source (you'll need requirments.txt or pipenv)::
|
||||
|
||||
pip install --no-binary :all: pycapnp
|
||||
|
||||
To force bundling libcapnp (or force system libcapnp), just in case setup.py isn't doing the right thing::
|
||||
|
||||
pip install --no-binary :all: --install-option "--force-bundled-libcapnp"
|
||||
pip install --no-binary :all: --install-option "--force-system-libcapnp"
|
||||
|
||||
If you're using an older Linux distro (e.g. CentOS 6) you many need to set `LDFLAGS="-Wl,--no-as-needed -lrt"`::
|
||||
|
||||
LDFLAGS="-Wl,--no-as-needed -lrt" pip install --no-binary :all: pycapnp
|
||||
|
||||
It's also possible to specify the libcapnp url when bundling (this may not work, there be dragons)::
|
||||
|
||||
pip install --no-binary :all: --install-option "--force-bundled-libcapnp" --install-option "--libcapnp-url" --install-option "https://github.com/capnproto/capnproto/archive/master.tar.gz"
|
||||
|
||||
From Source
|
||||
-----------
|
||||
Source installation is generally not needed unless you're looking into an issue with capnproto or pycapnp itself.
|
||||
|
||||
C++ Cap'n Proto Library
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
You need to install the C++ Cap'n Proto library first. It requires a C++ compiler with C++14 support, such as GCC 5+ or Clang 5+. Follow installation docs at `https://capnproto.org/install.html <https://capnproto.org/install.html>`_.
|
||||
|
||||
pycapnp from git
|
||||
~~~~~~~~~~~~~~~~
|
||||
If you want the latest development version, you can clone the github repo::
|
||||
|
||||
git clone https://github.com/capnproto/pycapnp.git
|
||||
|
||||
For development packages use one of the following to install the python dependencies::
|
||||
|
||||
pipenv install
|
||||
pip install -r requirements.txt
|
||||
|
||||
And install pycapnp with::
|
||||
|
||||
cd pycapnp
|
||||
pip install .
|
||||
|
||||
or::
|
||||
|
||||
cd pycapnp
|
||||
python setup.py install
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
Clone the repo from https://github.com/capnproto/pycapnp.git::
|
||||
|
||||
git clone https://github.com/capnproto/pycapnp.git
|
||||
|
||||
For development packages use one of the following to install the python dependencies::
|
||||
|
||||
pipenv install
|
||||
pip install -r requirements.txt
|
||||
|
||||
Building::
|
||||
|
||||
cd pycapnp
|
||||
pip install .
|
||||
|
||||
or::
|
||||
|
||||
cd pycapnp
|
||||
python setup.py install
|
||||
|
||||
Useful targets for setup.py::
|
||||
|
||||
python setup.py build
|
||||
python setup.py clean
|
||||
|
||||
Useful command-line arguments are available for setup.py::
|
||||
|
||||
--force-bundled-libcapnp
|
||||
--force-system-libcapnp
|
||||
--libcapnp-url
|
||||
|
||||
Testing is done through pytest::
|
||||
|
||||
cd pycapnp
|
||||
pytest
|
||||
pytest test/test_rpc_calculator.py
|
||||
|
||||
Once you're done installing, take a look at the :ref:`quickstart`
|
720
_sources/quickstart.rst.txt
Normal file
720
_sources/quickstart.rst.txt
Normal file
|
@ -0,0 +1,720 @@
|
|||
.. _quickstart:
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
This assumes you already have the capnp library installed. If you don't, please follow the instructions at :ref:`Installation <install>` first.
|
||||
|
||||
In general, this library is a very light wrapping of the `Cap'n Proto C++ library <https://capnproto.org/cxx.html>`_. You can refer to its docs for more advanced concepts, or just to get a basic idea of how the python library is structured.
|
||||
|
||||
|
||||
Load a Cap'n Proto Schema
|
||||
-------------------------
|
||||
First you need to import the library::
|
||||
|
||||
import capnp
|
||||
|
||||
Then you can load the Cap'n Proto schema with::
|
||||
|
||||
import addressbook_capnp
|
||||
|
||||
This will look all through all the directories in your sys.path/PYTHONPATH, and try to find a file of the form 'addressbook.capnp'. If you want to disable the import hook magic that `import capnp` adds, and load manually, here's how::
|
||||
|
||||
capnp.remove_import_hook()
|
||||
addressbook_capnp = capnp.load('addressbook.capnp')
|
||||
|
||||
For future reference, here is the Cap'n Proto schema. Also available in the github repository under `examples/addressbook.capnp <https://github.com/capnproto/pycapnp/tree/master/examples>`_::
|
||||
|
||||
# addressbook.capnp
|
||||
@0x934efea7f017fff0;
|
||||
|
||||
const qux :UInt32 = 123;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Text;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
|
||||
Const values
|
||||
~~~~~~~~~~~~
|
||||
Const values show up just as you'd expect under the loaded schema. For example::
|
||||
|
||||
print addressbook_capnp.qux
|
||||
# 123
|
||||
|
||||
|
||||
Build a message
|
||||
---------------
|
||||
|
||||
Initialize a New Cap'n Proto Object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that you've imported your schema, you need to allocate an actual struct from that schema. In this case, we will allocate an `AddressBook`::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
|
||||
Notice that we used `addressbook_capnp` from the previous section: `Load a Cap'n Proto Schema`_.
|
||||
|
||||
Also as a shortcut, you can pass keyword arguments to the `new_message` function, and those fields will be set in the new message::
|
||||
|
||||
person = addressbook_capnp.Person.new_message(name='alice')
|
||||
# is equivalent to:
|
||||
person = addressbook_capnp.Person.new_message()
|
||||
person.name = 'alice'
|
||||
|
||||
|
||||
List
|
||||
~~~~
|
||||
Allocating a list inside of an object requires use of the `init` function::
|
||||
|
||||
people = addresses.init('people', 2)
|
||||
|
||||
For now, let's grab the first element out of this list and assign it to a variable named `alice`::
|
||||
|
||||
alice = people[0]
|
||||
|
||||
.. note:: It is a very bad idea to call `init` more than once on a single field. Every call to `init` allocates new memory inside your Cap'n Proto message, and if you call it more than once, the previous memory is left as dead space in the message. See `Tips and Best Practices <https://capnproto.org/cxx.html#tips-and-best-practices>`_ for more details.
|
||||
|
||||
|
||||
Primitive Types
|
||||
~~~~~~~~~~~~~~~
|
||||
For all primitive types, from the Cap'n Proto docs:
|
||||
|
||||
- Boolean: Bool
|
||||
- Integers: Int8, Int16, Int32, Int64
|
||||
- Unsigned integers: UInt8, UInt16, UInt32, UInt64
|
||||
- Floating-point: Float32, Float64
|
||||
- Blobs: Text, Data
|
||||
|
||||
You can assign straight to the variable with the corresponding Python type. For Blobs, you use strings. Assignment happens just by using the `.` syntax on the object you contstructed above::
|
||||
|
||||
alice.id = 123
|
||||
alice.name = 'Alice'
|
||||
alice.email = 'alice@example.com'
|
||||
|
||||
.. note:: Text fields will behave differently depending on your version of Python. In Python 2.x, Text fields will expect and return a `bytes` string, while in Python 3.x, they will expect and return a `unicode` string. Data fields will always a return `bytes` string.
|
||||
|
||||
|
||||
Enums
|
||||
~~~~~
|
||||
First we'll allocate a length one list of phonenumbers for `alice`::
|
||||
|
||||
alicePhone = alice.init('phones', 1)[0]
|
||||
|
||||
Note that even though it was a length 1 list, it was still a list that was returned, and we extracted the first (and only) element with `[0]`.
|
||||
|
||||
Enums are treated like strings, and you assign to them like they were a Text field::
|
||||
|
||||
alicePhone.type = 'mobile'
|
||||
|
||||
If you assign an invalid value to one, you will get a ValueError::
|
||||
|
||||
alicePhone.type = 'foo'
|
||||
---------------------------------------------------------------------------
|
||||
ValueError Traceback (most recent call last)
|
||||
...
|
||||
ValueError: src/capnp/schema.c++:326: requirement not met: enum has no such enumerant; name = foo
|
||||
|
||||
|
||||
Unions
|
||||
~~~~~~
|
||||
For the most part, you just treat them like structs::
|
||||
|
||||
alice.employment.school = "MIT"
|
||||
|
||||
Now the `school` field is the active part of the union, and we've assigned `'MIT'` to it. You can query which field is set in a union with `which()`, shown in `Reading Unions`_
|
||||
|
||||
Also, one weird case is for Void types in Unions (and in general, but Void is really only used in Unions). For these, you will have to assign `None` to them::
|
||||
|
||||
bob.employment.unemployed = None
|
||||
|
||||
.. note:: One caveat for unions is having structs as union members. Let us assume `employment.school` was actually a struct with a field of type `Text` called `name`::
|
||||
|
||||
alice.employment.school.name = "MIT"
|
||||
# Raises a ValueError
|
||||
|
||||
The problem is that a struct within a union isn't initialized automatically. You have to do the following::
|
||||
|
||||
school = alice.employment.init('school')
|
||||
school.name = "MIT"
|
||||
|
||||
Note that this is similar to `init` for lists, but you don't pass a size. Requiring the `init` makes it more clear that a memory allocation is occurring, and will hopefully make you mindful that you shouldn't set more than 1 field inside of a union, else you risk a memory leak
|
||||
|
||||
|
||||
Writing to a File
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Once you're done assigning to all the fields in a message, you can write it to a file like so::
|
||||
|
||||
f = open('example.bin', 'w+b')
|
||||
addresses.write(f)
|
||||
|
||||
There is also a `write_packed` function, that writes out the message more space-efficientally. If you use write_packed, make sure to use read_packed when reading the message.
|
||||
|
||||
|
||||
Read a message
|
||||
--------------
|
||||
|
||||
Reading from a file
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Much like before, you will have to de-serialize the message from a file descriptor::
|
||||
|
||||
f = open('example.bin', 'rb')
|
||||
addresses = addressbook_capnp.AddressBook.read(f)
|
||||
|
||||
Note that this very much needs to match the type you wrote out. In general, you will always be sending the same message types out over a given channel or you should wrap all your types in an unnamed union. Unnamed unions are defined in the .capnp file like so::
|
||||
|
||||
struct Message {
|
||||
union {
|
||||
person @0 :Person;
|
||||
addressbook @1 :AddressBook;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reading Fields
|
||||
~~~~~~~~~~~~~~
|
||||
Fields are very easy to read. You just use the `.` syntax as before. Lists behave just like normal Python lists::
|
||||
|
||||
for person in addresses.people:
|
||||
print(person.name, ':', person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ':', phone.number)
|
||||
|
||||
|
||||
Reading Unions
|
||||
~~~~~~~~~~~~~~
|
||||
The only tricky one is unions, where you need to call `.which()` to determine the union type. The `.which()` call returns an enum, ie. a string, corresponding to the field name::
|
||||
|
||||
which = person.employment.which()
|
||||
print(which)
|
||||
|
||||
if which == 'unemployed':
|
||||
print('unemployed')
|
||||
elif which == 'employer':
|
||||
print('employer:', person.employment.employer)
|
||||
elif which == 'school':
|
||||
print('student at:', person.employment.school)
|
||||
elif which == 'selfEmployed':
|
||||
print('self employed')
|
||||
print()
|
||||
|
||||
|
||||
Serializing/Deserializing
|
||||
-------------------------
|
||||
|
||||
Files
|
||||
~~~~~
|
||||
As shown in the examples above, there is file serialization with `write()`::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
...
|
||||
f = open('example.bin', 'w+b')
|
||||
addresses.write(f)
|
||||
|
||||
And similarly for reading::
|
||||
|
||||
f = open('example.bin', 'rb')
|
||||
addresses = addressbook_capnp.AddressBook.read(f)
|
||||
|
||||
There are packed versions as well::
|
||||
|
||||
addresses.write_packed(f)
|
||||
f.seek(0)
|
||||
addresses = addressbook_capnp.AddressBook.read_packed(f)
|
||||
|
||||
|
||||
Multi-message files
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The above methods only guaranteed to work if your file contains a single message. If you have more than 1 message serialized sequentially in your file, then you need to use these convenience functions::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
...
|
||||
f = open('example.bin', 'w+b')
|
||||
addresses.write(f)
|
||||
addresses.write(f)
|
||||
addresses.write(f) # write 3 messages
|
||||
f.seek(0)
|
||||
|
||||
for addresses in addressbook_capnp.AddressBook.read_multiple(f):
|
||||
print addresses
|
||||
|
||||
There is also a packed version::
|
||||
|
||||
for addresses in addressbook_capnp.AddressBook.read_multiple_packed(f):
|
||||
print addresses
|
||||
|
||||
Dictionaries
|
||||
~~~~~~~~~~~~
|
||||
There is a convenience method for converting Cap'n Proto messages to a dictionary. This works for both Builder and Reader type messages::
|
||||
|
||||
alice.to_dict()
|
||||
|
||||
For the reverse, all you have to do is pass keyword arguments to the new_message constructor::
|
||||
|
||||
my_dict = {'name' : 'alice'}
|
||||
alice = addressbook_capnp.Person.new_message(**my_dict)
|
||||
# equivalent to: alice = addressbook_capnp.Person.new_message(name='alice')
|
||||
|
||||
It's also worth noting, you can use python lists/dictionaries interchangably with their Cap'n Proto equivalent types::
|
||||
|
||||
book = addressbook_capnp.AddressBook.new_message(people=[{'name': 'Alice'}])
|
||||
...
|
||||
book = addressbook_capnp.AddressBook.new_message()
|
||||
book.init('people', 1)
|
||||
book.people[0] = {'name': 'Bob'}
|
||||
|
||||
|
||||
Byte Strings/Buffers
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
There is serialization to a byte string available::
|
||||
|
||||
encoded_message = alice.to_bytes()
|
||||
|
||||
And a corresponding from_bytes function::
|
||||
|
||||
alice = addressbook_capnp.Person.from_bytes(encoded_message)
|
||||
|
||||
There are also packed versions::
|
||||
|
||||
alice2 = addressbook_capnp.Person.from_bytes_packed(alice.to_bytes_packed())
|
||||
|
||||
|
||||
Byte Segments
|
||||
~~~~~~~~~~~~~
|
||||
.. note:: This feature is not supported in PyPy at the moment, pending investigation.
|
||||
|
||||
Cap'n Proto supports a serialization mode which minimizes object copies. In the C++ interface, ``capnp::MessageBuilder::getSegmentsForOutput()`` returns an array of pointers to segments of the message's content without copying. ``capnp::SegmentArrayMessageReader`` performs the reverse operation, i.e., takes an array of pointers to segments and uses the underlying data, again without copying. This produces a different wire serialization format from ``to_bytes()`` serialization, which uses ``capnp::messageToFlatArray()`` and ``capnp::FlatArrayMessageReader`` (both of which use segments internally, but write them in an incompatible way).
|
||||
|
||||
For compatibility on the Python side, use the ``to_segments()`` and ``from_segments()`` functions::
|
||||
|
||||
segments = alice.to_segments()
|
||||
|
||||
This returns a list of segments, each a byte buffer. Each segment can be, e.g., turned into a ZeroMQ message frame. The list of segments can also be turned back into an object::
|
||||
|
||||
alice = addressbook_capnp.Person.from_segments(segments)
|
||||
|
||||
For more information, please refer to the following links:
|
||||
|
||||
- `Advice on minimizing copies from Cap'n Proto <https://stackoverflow.com/questions/28149139/serializing-mutable-state-and-sending-it-asynchronously-over-the-network-with-ne/28156323#28156323>`_ (from the author of Cap'n Proto)
|
||||
- `Advice on using Cap'n Proto over ZeroMQ <https://stackoverflow.com/questions/32041315/how-to-send-capn-proto-message-over-zmq/32042234#32042234>`_ (from the author of Cap'n Proto)
|
||||
- `Discussion about sending and reassembling Cap'n Proto message segments in C++ <https://groups.google.com/forum/#!topic/capnproto/ClDjGbO7egA>`_ (from the Cap'n Proto mailing list; includes sample code)
|
||||
|
||||
|
||||
RPC
|
||||
---
|
||||
|
||||
Cap'n Proto has a rich RPC protocol. You should read the `RPC specification <https://capnproto.org/rpc.html>`_ as well as the `C++ RPC documentation <http://kentonv.github.io/capnproto/cxxrpc.html>`_ before using pycapnp's RPC features. As with the serialization part of this library, the RPC component tries to be a very thin wrapper on top of the C++ API.
|
||||
|
||||
The examples below will be using `calculator.capnp <https://github.com/capnproto/pycapnp/blob/master/examples/calculator.capnp>`_. Please refer to it to understand the interfaces that will be used.
|
||||
|
||||
Asyncio support was added to pycapnp in v1.0.0 utilizing the TwoWayPipe interface to libcapnp (instead of having libcapnp control the socket communication). The main advantage here is that standard Python socket libraries can be used with pycapnp (more importantly, TLS/SSL). Asyncio requires a bit more boiler plate to get started but it does allow for a lot more control than using the pycapnp socket wrapper.
|
||||
|
||||
|
||||
Client
|
||||
~~~~~~
|
||||
|
||||
There are two ways to start a client: libcapnp socket wrapper and asyncio.
|
||||
The wrapper is easier to implement but is very limited (doesn't support SSL/TLS with Python).
|
||||
asyncio requires more setup and can be harder to debug; however, it does support SSL/TLS and has more control over the socket error conditions. asyncio also helps get around the threading limitations around the current pycapnp implementation has with libcapnp (pycapnp objects and functions must all be in the same thread).
|
||||
|
||||
|
||||
Starting a Client
|
||||
#################
|
||||
Starting a client is very easy::
|
||||
|
||||
import capnp
|
||||
import calculator_capnp
|
||||
|
||||
client = capnp.TwoPartyClient('localhost:60000')
|
||||
|
||||
.. note:: You can also pass a raw socket with a `fileno()` method to TwoPartyClient
|
||||
.. note:: This will not work with SSL/TLS, please see :ref:`rpc-asyncio-client`
|
||||
|
||||
|
||||
.. _rpc-asyncio-client:
|
||||
|
||||
Starting a Client (asyncio)
|
||||
###########################
|
||||
Asyncio takes a bit more boilerplate than using the socket wrapper, but it gives you a lot more control. The example here is very simplistic. Here's an example of full error handling (with reconnection on server failure): `hidio client <https://github.com/hid-io/hid-io-core/blob/master/python/hidiocore/client/__init__.py>`_.
|
||||
|
||||
At a basic level, asyncio splits the input and output streams of the tcp socket and sends it to the libcapnp TwoWayPipe interface. An async reader Python function/method is used to consume the incoming byte stream and an async writer Python function/method is used to write outgoing bytes to the socket.
|
||||
|
||||
.. note:: You'll need to be using the async keyword on some of the Python function/methods. If you're unsure, look at the full `example code <https://github.com/capnproto/pycapnp/blob/master/examples/async_calculator_client.py>`_. Also, read up on recent Python asyncio tutorials if you're new to the concept. Make sure the tutorial is 3.7+, asyncio changed a lot from when it was first introduced in 3.4.
|
||||
|
||||
First you'll need two basic async functions::
|
||||
|
||||
async def myreader(client, reader):
|
||||
while True:
|
||||
data = await reader.read(4096)
|
||||
client.write(data)
|
||||
|
||||
|
||||
async def mywriter(client, writer):
|
||||
while True:
|
||||
data = await client.read(4096)
|
||||
writer.write(data.tobytes())
|
||||
await writer.drain()
|
||||
|
||||
.. note:: There's no socket error handling here, so this won't be sufficient for anything beyond a simple example.
|
||||
|
||||
Next you'll need to define an async function that sets up the socket connection. This is equivalent to `client = capnp.TwoPartyClient('localhost:60000')` in the earlier example::
|
||||
|
||||
async def main(host):
|
||||
addr = 'localhost'
|
||||
port = '6000'
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
reader, writer = await asyncio.open_connection(
|
||||
addr, port,
|
||||
family=socket.AF_INET
|
||||
)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
reader, writer = await asyncio.open_connection(
|
||||
addr, port,
|
||||
family=socket.AF_INET6
|
||||
)
|
||||
|
||||
# Start TwoPartyClient using TwoWayPipe (takes no arguments in this mode)
|
||||
client = capnp.TwoPartyClient()
|
||||
|
||||
# Assemble reader and writer tasks, run in the background
|
||||
coroutines = [myreader(client, reader), mywriter(client, writer)]
|
||||
asyncio.gather(*coroutines, return_exceptions=True)
|
||||
|
||||
## Bootstrap Here ##
|
||||
|
||||
.. note:: On systems that have both IPv4 and IPv6 addresses, IPv6 is often resolved first and needs to be handled separately. If you're certain IPv6 won't be used, you can remove it (you should also avoid localhost, and stick to something like 127.0.0.1).
|
||||
|
||||
Finally, you'll need to start the asyncio function::
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main(parse_args().host))
|
||||
|
||||
.. note:: This is the simplest way to start asyncio and usually not sufficient for most applications.
|
||||
|
||||
|
||||
SSL/TLS Client
|
||||
^^^^^^^^^^^^^^
|
||||
SSL/TLS setup effectively wraps the socket transport. You'll need an SSL certificate, for this example we'll be using a self-signed certificate. Most of the asyncio setup is the same as above::
|
||||
|
||||
async def main(host):
|
||||
addr = 'localhost'
|
||||
port = '6000'
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=os.path.join(this_dir, 'selfsigned.cert'))
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
reader, writer = await asyncio.open_connection(
|
||||
addr, port,
|
||||
ssl=ctx,
|
||||
family=socket.AF_INET
|
||||
)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
reader, writer = await asyncio.open_connection(
|
||||
addr, port,
|
||||
ssl=ctx,
|
||||
family=socket.AF_INET6
|
||||
)
|
||||
|
||||
# Start TwoPartyClient using TwoWayPipe (takes no arguments in this mode)
|
||||
client = capnp.TwoPartyClient()
|
||||
|
||||
# Assemble reader and writer tasks, run in the background
|
||||
coroutines = [myreader(client, reader), mywriter(client, writer)]
|
||||
asyncio.gather(*coroutines, return_exceptions=True)
|
||||
|
||||
## Bootstrap Here ##
|
||||
|
||||
Due to a `bug <https://bugs.python.org/issue36709>`_ in Python 3.7 and 3.8 asyncio client needs to be initialized in a slightly different way::
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(main(parse_args().host))
|
||||
|
||||
|
||||
Bootstrap
|
||||
#########
|
||||
Before calling any methods you'll need to bootstrap the Calculator interface::
|
||||
|
||||
calculator = client.bootstrap().cast_as(calculator_capnp.Calculator)
|
||||
|
||||
There's two things worth noting here. First, we are asking for the server capability. Secondly, you see that we are casting the capability that we receive. This is because capabilities are intrinsically dynamic, and they hold no run time type information, so we need to pick what interface to interpret them as.
|
||||
|
||||
|
||||
Calling methods
|
||||
###############
|
||||
There are 2 ways to call RPC methods. First the more verbose `request` syntax::
|
||||
|
||||
request = calculator.evaluate_request()
|
||||
request.expression.literal = 123
|
||||
eval_promise = request.send()
|
||||
|
||||
This creates a request for the method named 'evaluate', sets `expression.literal` in that call's parameters to 123, and then sends the request and returns a promise (all non-blocking).
|
||||
|
||||
The shorter syntax for calling methods is::
|
||||
|
||||
eval_promise = calculator.evaluate({"literal": 123})
|
||||
|
||||
The major shortcoming with this method is that expressing complex fields with many nested sub-structs can become very tedious.
|
||||
|
||||
Once you have a promise, there are 2 ways of getting to the result. The first is to wait for it::
|
||||
|
||||
result = eval_promise.wait()
|
||||
|
||||
The second is to build a promise chain by calling `then`::
|
||||
|
||||
def do_stuff(val):
|
||||
...
|
||||
|
||||
eval_promise.then(do_stuff).wait()
|
||||
|
||||
|
||||
Pipelining
|
||||
##########
|
||||
If a method returns values that are themselves capabilites, then you can access these fields before having to call `wait`. Doing this is called pipelining, and it allows Cap'n Proto to chain the calls without a round-trip occurring to the server::
|
||||
|
||||
# evaluate returns `value` which is itself an interface.
|
||||
# You can call a new method on `value` without having to call wait first
|
||||
read_promise = eval_promise.value.read()
|
||||
read_result = read_promise.wait() # only 1 wait call
|
||||
|
||||
You can also chain promises with `then` and the same pipelining will occur::
|
||||
|
||||
read_result = eval_promise.then(lambda ret: ret.value.read()).wait()
|
||||
|
||||
|
||||
Server
|
||||
~~~~~~
|
||||
There are two ways to start a server: libcapnp socket wrapper and asyncio.
|
||||
The wrapper is easier to implement but is very limited (doesn't support SSL/TLS with Python).
|
||||
asyncio requires more setup and can be harder to debug; however, it does support SSL/TLS and has more control over the socket error conditions. asyncio also helps get around the threading limitations around the current pycapnp implementation has with libcapnp (pycapnp objects and functions must all be in the same thread). The asyncio Server is a bit more work to implement than an asyncio client as more error handling is required to deal with client connection/disconnection/timeout events.
|
||||
|
||||
|
||||
Starting a Server
|
||||
#################
|
||||
|
||||
To start a server::
|
||||
|
||||
server = capnp.TwoPartyServer('*:60000', bootstrap=CalculatorImpl())
|
||||
server.run_forever()
|
||||
|
||||
.. note:: You can also pass a raw socket with a `fileno()` method to TwoPartyServer. In that case, `run_forever` will not work, and you will have to use `on_disconnect.wait()`.
|
||||
.. note:: This will not work with SSL/TLS, please see :ref:`rpc-asyncio-server`
|
||||
|
||||
|
||||
.. _rpc-asyncio-server:
|
||||
|
||||
Starting a Server (asyncio)
|
||||
###########################
|
||||
Like the asyncio client, an asyncio server takes a bunch of boilerplate as opposed to using the socket wrapper. Servers generally have to handle a lot more error conditions than clients so they are generally more complicated to implement with asyncio.
|
||||
|
||||
Just like the asyncio client, both the input and output socket streams are handled by reader/writer callback functions/methods.
|
||||
|
||||
.. note:: You'll need to be using the async keyword on some of the Python function/methods. If you're unsure, look at the full `example code <https://github.com/capnproto/pycapnp/blob/master/examples/async_calculator_client.py>`_. Also, read up on recent Python asyncio tutorials if you're new to the concept. Make sure the tutorial is 3.7+, asyncio changed a lot from when it was first introduced in 3.4.
|
||||
|
||||
To simplify the callbacks use a server class to define the reader/writer callbacks.::
|
||||
|
||||
class Server:
|
||||
async def myreader(self):
|
||||
while self.retry:
|
||||
try:
|
||||
# Must be a wait_for so we don't block on read()
|
||||
data = await asyncio.wait_for(
|
||||
self.reader.read(4096),
|
||||
timeout=0.1
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
print("myreader timeout.")
|
||||
continue
|
||||
except Exception as err:
|
||||
print("Unknown myreader err: %s", err)
|
||||
return False
|
||||
await self.server.write(data)
|
||||
print("myreader done.")
|
||||
return True
|
||||
|
||||
async def mywriter(self):
|
||||
while self.retry:
|
||||
try:
|
||||
# Must be a wait_for so we don't block on read()
|
||||
data = await asyncio.wait_for(
|
||||
self.server.read(4096),
|
||||
timeout=0.1
|
||||
)
|
||||
self.writer.write(data.tobytes())
|
||||
except asyncio.TimeoutError:
|
||||
print("mywriter timeout.")
|
||||
continue
|
||||
except Exception as err:
|
||||
print("Unknown mywriter err: %s", err)
|
||||
return False
|
||||
print("mywriter done.")
|
||||
return True
|
||||
|
||||
We need an additional `myserver()` method in the `Server` class to handle each of the incoming socket connections::
|
||||
|
||||
async def myserver(self, reader, writer):
|
||||
# Start TwoPartyServer using TwoWayPipe (only requires bootstrap)
|
||||
self.server = capnp.TwoPartyServer(bootstrap=CalculatorImpl())
|
||||
self.reader = reader
|
||||
self.writer = writer
|
||||
self.retry = True
|
||||
|
||||
# Assemble reader and writer tasks, run in the background
|
||||
coroutines = [self.myreader(), self.mywriter()]
|
||||
tasks = asyncio.gather(*coroutines, return_exceptions=True)
|
||||
|
||||
while True:
|
||||
self.server.poll_once()
|
||||
# Check to see if reader has been sent an eof (disconnect)
|
||||
if self.reader.at_eof():
|
||||
self.retry = False
|
||||
break
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
# Make wait for reader/writer to finish (prevent possible resource leaks)
|
||||
await tasks
|
||||
|
||||
Finally, we'll need to start an asyncio server to spawn a new async `myserver()` with it's own `Server()` object for each new connection::
|
||||
|
||||
async def new_connection(reader, writer):
|
||||
server = Server()
|
||||
await server.myserver(reader, writer)
|
||||
|
||||
async def main():
|
||||
addr = 'localhost'
|
||||
port = '60000'
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
server = await asyncio.start_server(
|
||||
new_connection,
|
||||
addr, port,
|
||||
family=socket.AF_INET
|
||||
)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
server = await asyncio.start_server(
|
||||
new_connection,
|
||||
addr, port,
|
||||
family=socket.AF_INET6
|
||||
)
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
||||
.. note:: On systems that have both IPv4 and IPv6 addresses, IPv6 is often resolved first and needs to be handled separately. If you're certain IPv6 won't be used, you can remove it (you should also avoid localhost, and stick to something like 127.0.0.1). If you're broadcasting in general, you'll probably want to use `0.0.0.0` (IPv4) or `::/0` (IPv6).
|
||||
|
||||
|
||||
SSL/TLS Server
|
||||
^^^^^^^^^^^^^^
|
||||
Adding SSL/TLS support for a pycapnp asyncio server is fairly straight-forward. Just create an SSL context before starting the asyncio server::
|
||||
|
||||
async def main():
|
||||
addr = 'localhost'
|
||||
port = '60000'
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ctx.load_cert_chain(os.path.join(this_dir, 'selfsigned.cert'), os.path.join(this_dir, 'selfsigned.key'))
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
server = await asyncio.start_server(
|
||||
new_connection,
|
||||
addr, port,
|
||||
ssl=ctx,
|
||||
family=socket.AF_INET
|
||||
)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
server = await asyncio.start_server(
|
||||
new_connection,
|
||||
addr, port,
|
||||
ssl=ctx,
|
||||
family=socket.AF_INET6
|
||||
)
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
Implementing a Server
|
||||
#####################
|
||||
Here's a part of how you would implement a Calculator server::
|
||||
|
||||
class CalculatorImpl(calculator_capnp.Calculator.Server):
|
||||
|
||||
"Implementation of the Calculator Cap'n Proto interface."
|
||||
|
||||
def evaluate(self, expression, _context, **kwargs):
|
||||
return evaluate_impl(expression).then(lambda value: setattr(_context.results, 'value', ValueImpl(value)))
|
||||
|
||||
def defFunction_context(self, context):
|
||||
params = context.params
|
||||
context.results.func = FunctionImpl(params.paramCount, params.body)
|
||||
|
||||
def getOperator(self, op, **kwargs):
|
||||
return OperatorImpl(op)
|
||||
|
||||
Some major things worth noting.
|
||||
|
||||
- You must inherit from `your_module_capnp.YourInterface.Server`, but don't worry about calling __super__ in your __init__
|
||||
- Method names of your class must either match the interface exactly, or have '_context' appended to it
|
||||
- If your method name is exactly the same as the interface, then you will be passed all the arguments from the interface as keyword arguments, so your argument names must match the interface spec exactly. You will also receive a `_context` parameter which is equivalent to the C++ API's Context. I highly recommend having `**kwargs` as well, so that even if your interface spec is upgraded and arguments were added, your server will still operate fine.
|
||||
- Returns work with a bit of magic as well. If you return a promise, then it will be handled the same as if you returned a promise from a server method in the C++ API. Otherwise, your return statement will be filled into the results struct following the ordering in your spec, for example::
|
||||
|
||||
# capability.capnp file
|
||||
interface TestInterface {
|
||||
foo @0 (i :UInt32, j :Bool) -> (x: Text, i:UInt32);
|
||||
}
|
||||
|
||||
# python code
|
||||
class TestInterface(capability_capnp.TestInterface.Server):
|
||||
def foo(self, i, j, **kwargs):
|
||||
return str(j), i
|
||||
|
||||
- If your method ends in _context, then you will only be passed a context parameter. You will have to access params and set results yourself manually. Returning promises still works as above, but you can't return anything else from a method.
|
||||
|
||||
|
||||
Full Examples
|
||||
-------------
|
||||
`Full examples <https://github.com/capnproto/pycapnp/blob/master/examples>`_ are available on github. There is also an example of a very simplistic RPC available in `test_rpc.py <https://github.com/capnproto/pycapnp/blob/master/test/test_rpc.py>`_.
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -52,6 +52,8 @@ div.sphinxsidebar {
|
|||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap : break-word;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
|
@ -79,14 +81,26 @@ div.sphinxsidebar input {
|
|||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox form.search {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="text"] {
|
||||
width: 170px;
|
||||
float: left;
|
||||
width: 80%;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
||||
width: 30px;
|
||||
float: left;
|
||||
width: 20%;
|
||||
border-left: none;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
|
@ -124,6 +138,8 @@ ul.keywordmatches li.goodmatch a {
|
|||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
|
@ -151,9 +167,14 @@ table.indextable td {
|
|||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
table.indextable ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
table.indextable > tbody > tr > td > ul {
|
||||
padding-left: 0em;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
|
@ -185,19 +206,51 @@ div.genindex-jumpbox {
|
|||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- domain module index --------------------------------------------------- */
|
||||
|
||||
table.modindextable td {
|
||||
padding: 2px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
div.body {
|
||||
min-width: 450px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
a.brackets:before,
|
||||
span.brackets > a:before{
|
||||
content: "[";
|
||||
}
|
||||
|
||||
a.brackets:after,
|
||||
span.brackets > a:after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink {
|
||||
dt:hover > a.headerlink,
|
||||
caption:hover > a.headerlink,
|
||||
p.caption:hover > a.headerlink,
|
||||
div.code-block-caption:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
@ -209,10 +262,6 @@ div.body td {
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
@ -240,6 +289,12 @@ img.align-center, .figure.align-center, object.align-center {
|
|||
margin-right: auto;
|
||||
}
|
||||
|
||||
img.align-default, .figure.align-default {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
@ -248,6 +303,10 @@ img.align-center, .figure.align-center, object.align-center {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.align-default {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -257,22 +316,29 @@ img.align-center, .figure.align-center, object.align-center {
|
|||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px 7px 0 7px;
|
||||
padding: 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
clear: right;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition, div.topic, pre, div[class|="highlight"] {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px 7px 0 7px;
|
||||
padding: 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
|
@ -287,16 +353,13 @@ div.admonition {
|
|||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
|
@ -307,13 +370,40 @@ div.body p.centered {
|
|||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- content of sidebars/topics/admonitions -------------------------------- */
|
||||
|
||||
div.sidebar > :last-child,
|
||||
div.topic > :last-child,
|
||||
div.admonition > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-default {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table caption span.caption-text {
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
|
@ -322,10 +412,6 @@ table.docutils td, table.docutils th {
|
|||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
@ -344,6 +430,67 @@ table.citation td {
|
|||
border-bottom: none;
|
||||
}
|
||||
|
||||
th > :first-child,
|
||||
td > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
th > :last-child,
|
||||
td > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/* -- figures --------------------------------------------------------------- */
|
||||
|
||||
div.figure {
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.figure p.caption {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-text {
|
||||
}
|
||||
|
||||
/* -- field list styles ----------------------------------------------------- */
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
-moz-hyphens: manual;
|
||||
-ms-hyphens: manual;
|
||||
-webkit-hyphens: manual;
|
||||
hyphens: manual;
|
||||
}
|
||||
|
||||
/* -- hlist styles ---------------------------------------------------------- */
|
||||
|
||||
table.hlist {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
table.hlist td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
|
@ -366,11 +513,75 @@ ol.upperroman {
|
|||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
ol > li:first-child > :first-child,
|
||||
ul > li:first-child > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
ol ol > li:first-child > :first-child,
|
||||
ol ul > li:first-child > :first-child,
|
||||
ul ol > li:first-child > :first-child,
|
||||
ul ul > li:first-child > :first-child {
|
||||
margin-top: revert;
|
||||
}
|
||||
|
||||
ol > li:last-child > :last-child,
|
||||
ul > li:last-child > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
ol ol > li:last-child > :last-child,
|
||||
ol ul > li:last-child > :last-child,
|
||||
ul ol > li:last-child > :last-child,
|
||||
ul ul > li:last-child > :last-child {
|
||||
margin-bottom: revert;
|
||||
}
|
||||
|
||||
dl.footnote > dt,
|
||||
dl.citation > dt {
|
||||
float: left;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
dl.footnote > dd,
|
||||
dl.citation > dd {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl.footnote > dd:after,
|
||||
dl.citation > dd:after {
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
|
||||
dl.field-list {
|
||||
display: grid;
|
||||
grid-template-columns: fit-content(30%) auto;
|
||||
}
|
||||
|
||||
dl.field-list > dt {
|
||||
font-weight: bold;
|
||||
word-break: break-word;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
dl.field-list > dt:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
dl.field-list > dd {
|
||||
padding-left: 0.5em;
|
||||
margin-top: 0em;
|
||||
margin-left: 0em;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
dd > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
|
@ -384,28 +595,32 @@ dd {
|
|||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, .highlighted {
|
||||
dl > dd:last-child,
|
||||
dl > dd:last-child > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt:target, span.highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
rect.highlighted {
|
||||
fill: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.sig-paren {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -444,6 +659,12 @@ dl.glossary dt {
|
|||
font-style: oblique;
|
||||
}
|
||||
|
||||
.classifier:before {
|
||||
font-style: normal;
|
||||
margin: 0.5em;
|
||||
content: ":";
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
|
@ -456,37 +677,105 @@ pre {
|
|||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
span.pre {
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
-webkit-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
div[class^="highlight-"] {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.highlighttable tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.highlighttable tr {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
table.highlighttable td.linenos {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td.code {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.highlight .hll {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.highlight pre,
|
||||
table.highlighttable pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption + div {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption {
|
||||
margin-top: 1em;
|
||||
padding: 2px 5px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.code-block-caption code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos,
|
||||
div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-number {
|
||||
padding: 0.1em 0.3em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-text {
|
||||
}
|
||||
|
||||
div.literal-block-wrapper {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
code.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
code.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
code.xref, a code {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
@ -518,6 +807,15 @@ span.eqno {
|
|||
float: right;
|
||||
}
|
||||
|
||||
span.eqno a.headerlink {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div.math:hover a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Sphinx JavaScript utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -45,7 +45,7 @@ jQuery.urlencode = encodeURIComponent;
|
|||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s == 'undefined')
|
||||
if (typeof s === 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
|
@ -66,31 +66,80 @@ jQuery.getQueryParameters = function(s) {
|
|||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
function highlight(node, addItems) {
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.className = className;
|
||||
}
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
if (isInSVG) {
|
||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
var bbox = node.parentElement.getBBox();
|
||||
rect.x.baseVal.value = bbox.x;
|
||||
rect.y.baseVal.value = bbox.y;
|
||||
rect.width.baseVal.value = bbox.width;
|
||||
rect.height.baseVal.value = bbox.height;
|
||||
rect.setAttribute('class', className);
|
||||
addItems.push({
|
||||
"parent": node.parentNode,
|
||||
"target": rect});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this);
|
||||
highlight(this, addItems);
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
var addItems = [];
|
||||
var result = this.each(function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
for (var i = 0; i < addItems.length; ++i) {
|
||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
|
@ -100,27 +149,30 @@ var Documentation = {
|
|||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
|
||||
this.initOnKeyListeners();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
|
||||
PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
|
||||
LOCALE : 'unknown',
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated == 'undefined')
|
||||
if (typeof translated === 'undefined')
|
||||
return string;
|
||||
return (typeof translated == 'string') ? translated : translated[0];
|
||||
return (typeof translated === 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated == 'undefined')
|
||||
if (typeof translated === 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
@ -152,6 +204,7 @@ var Documentation = {
|
|||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash && $.browser.mozilla)
|
||||
|
@ -190,7 +243,7 @@ var Documentation = {
|
|||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
if (src.substr(-9) === 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
|
@ -222,11 +275,35 @@ var Documentation = {
|
|||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this == '..')
|
||||
if (this === '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
},
|
||||
|
||||
initOnKeyListeners: function() {
|
||||
$(document).keydown(function(event) {
|
||||
var activeElementType = document.activeElement.tagName;
|
||||
// don't navigate when in search box or textarea
|
||||
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
|
||||
&& !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
||||
switch (event.keyCode) {
|
||||
case 37: // left
|
||||
var prevHref = $('link[rel="prev"]').prop('href');
|
||||
if (prevHref) {
|
||||
window.location.href = prevHref;
|
||||
return false;
|
||||
}
|
||||
case 39: // right
|
||||
var nextHref = $('link[rel="next"]').prop('href');
|
||||
if (nextHref) {
|
||||
window.location.href = nextHref;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
12
_static/documentation_options.js
Normal file
12
_static/documentation_options.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '1.0.0b2',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
FILE_SUFFIX: '.html',
|
||||
LINK_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false
|
||||
};
|
BIN
_static/file.png
BIN
_static/file.png
Binary file not shown.
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 286 B |
10872
_static/jquery-3.5.1.js
vendored
Normal file
10872
_static/jquery-3.5.1.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
4
_static/jquery.js
vendored
4
_static/jquery.js
vendored
File diff suppressed because one or more lines are too long
297
_static/language_data.js
Normal file
297
_static/language_data.js
Normal file
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* language_data.js
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This script contains the language-specific data used by searchtools.js,
|
||||
* namely the list of stopwords, stemmer, scorer and splitter.
|
||||
*
|
||||
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var splitChars = (function() {
|
||||
var result = {};
|
||||
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
|
||||
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
|
||||
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
|
||||
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
|
||||
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
|
||||
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
|
||||
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
|
||||
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
|
||||
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
|
||||
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
|
||||
var i, j, start, end;
|
||||
for (i = 0; i < singles.length; i++) {
|
||||
result[singles[i]] = true;
|
||||
}
|
||||
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
|
||||
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
|
||||
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
|
||||
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
|
||||
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
|
||||
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
|
||||
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
|
||||
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
|
||||
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
|
||||
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
|
||||
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
|
||||
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
|
||||
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
|
||||
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
|
||||
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
|
||||
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
|
||||
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
|
||||
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
|
||||
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
|
||||
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
|
||||
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
|
||||
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
|
||||
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
|
||||
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
|
||||
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
|
||||
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
|
||||
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
|
||||
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
|
||||
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
|
||||
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
|
||||
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
|
||||
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
|
||||
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
|
||||
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
|
||||
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
|
||||
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
|
||||
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
|
||||
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
|
||||
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
|
||||
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
|
||||
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
|
||||
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
|
||||
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
|
||||
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
|
||||
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
|
||||
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
|
||||
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
|
||||
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
|
||||
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
|
||||
for (i = 0; i < ranges.length; i++) {
|
||||
start = ranges[i][0];
|
||||
end = ranges[i][1];
|
||||
for (j = start; j <= end; j++) {
|
||||
result[j] = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
function splitQuery(query) {
|
||||
var result = [];
|
||||
var start = -1;
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
if (splitChars[query.charCodeAt(i)]) {
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start, i));
|
||||
start = -1;
|
||||
}
|
||||
} else if (start === -1) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 199 B After Width: | Height: | Size: 90 B |
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Sphinx stylesheet -- nature theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -16,7 +16,7 @@
|
|||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
background-color: #111;
|
||||
background-color: #fff;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -125,10 +125,11 @@ div.sphinxsidebar input {
|
|||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type=text]{
|
||||
div.sphinxsidebar .searchformwrapper {
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
|
@ -183,10 +184,6 @@ div.admonition p.admonition-title + p {
|
|||
display: inline;
|
||||
}
|
||||
|
||||
div.highlight{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
|
@ -216,8 +213,6 @@ p.admonition-title:after {
|
|||
|
||||
pre {
|
||||
padding: 10px;
|
||||
background-color: White;
|
||||
color: #222;
|
||||
line-height: 1.2em;
|
||||
border: 1px solid #C6C9CB;
|
||||
font-size: 1.1em;
|
||||
|
@ -226,7 +221,7 @@ pre {
|
|||
-moz-box-shadow: 1px 1px 1px #d8d8d8;
|
||||
}
|
||||
|
||||
tt {
|
||||
code {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
|
@ -242,4 +237,10 @@ div.viewcode-block:target {
|
|||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
||||
|
||||
div.code-block-caption {
|
||||
background-color: #ddd;
|
||||
color: #222;
|
||||
border: 1px solid #C6C9CB;
|
||||
}
|
BIN
_static/plus.png
BIN
_static/plus.png
Binary file not shown.
Before Width: | Height: | Size: 199 B After Width: | Height: | Size: 90 B |
|
@ -4,8 +4,10 @@
|
|||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
|
||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
||||
.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
|
||||
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
|
@ -45,8 +47,10 @@
|
|||
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
|
||||
.highlight .sa { color: #4070a0 } /* Literal.String.Affix */
|
||||
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */
|
||||
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
|
@ -57,7 +61,9 @@
|
|||
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.highlight .fm { color: #06287e } /* Name.Function.Magic */
|
||||
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
|
@ -1,229 +1,53 @@
|
|||
/*
|
||||
* searchtools.js_t
|
||||
* searchtools.js
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilties for the full-text search.
|
||||
* Sphinx JavaScript utilities for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
if (!Scorer) {
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
// query found in title
|
||||
title: 15,
|
||||
partialTitle: 7,
|
||||
// query found in terms
|
||||
term: 5,
|
||||
partialTerm: 2
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
// query found in terms
|
||||
term: 5
|
||||
};
|
||||
|
||||
if (!splitQuery) {
|
||||
function splitQuery(query) {
|
||||
return query.split(/\s+/);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
|
@ -234,6 +58,19 @@ var Search = {
|
|||
_queued_query : null,
|
||||
_pulse_status : -1,
|
||||
|
||||
htmlToText : function(htmlString) {
|
||||
var htmlElement = document.createElement('span');
|
||||
htmlElement.innerHTML = htmlString;
|
||||
$(htmlElement).find('.headerlink').remove();
|
||||
docContent = $(htmlElement).find('[role=main]')[0];
|
||||
if(docContent === undefined) {
|
||||
console.warn("Content block not found. Sphinx search tries to obtain it " +
|
||||
"via '[role=main]'. Could you check your theme or template.");
|
||||
return "";
|
||||
}
|
||||
return docContent.textContent || docContent.innerText;
|
||||
},
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
|
@ -298,7 +135,7 @@ var Search = {
|
|||
this.out = $('#search-results');
|
||||
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
|
||||
this.dots = $('<span></span>').appendTo(this.title);
|
||||
this.status = $('<p style="display: none"></p>').appendTo(this.out);
|
||||
this.status = $('<p class="search-summary"> </p>').appendTo(this.out);
|
||||
this.output = $('<ul class="search"/>').appendTo(this.out);
|
||||
|
||||
$('#search-progress').text(_('Preparing search...'));
|
||||
|
@ -316,14 +153,13 @@ var Search = {
|
|||
*/
|
||||
query : function(query) {
|
||||
var i;
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
// stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = query.split(/\s+/);
|
||||
var tmp = splitQuery(query);
|
||||
var objectterms = [];
|
||||
for (i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] !== "") {
|
||||
|
@ -337,6 +173,10 @@ var Search = {
|
|||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i].toLowerCase());
|
||||
// prevent stemmer from cutting word smaller than two chars
|
||||
if(word.length < 3 && tmp[i].length >= 3) {
|
||||
word = tmp[i];
|
||||
}
|
||||
var toAppend;
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
|
@ -373,8 +213,7 @@ var Search = {
|
|||
}
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term))
|
||||
.concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title));
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
|
@ -411,7 +250,9 @@ var Search = {
|
|||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
|
||||
var requestUrl = "";
|
||||
var linkUrl = "";
|
||||
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
if (dirname.match(/\/index\/$/)) {
|
||||
|
@ -419,15 +260,17 @@ var Search = {
|
|||
} else if (dirname == 'index/') {
|
||||
dirname = '';
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
|
||||
linkUrl = requestUrl;
|
||||
|
||||
} else {
|
||||
// normal html builders
|
||||
listItem.append($('<a/>').attr('href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
|
||||
linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
linkUrl +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
|
@ -435,11 +278,11 @@ var Search = {
|
|||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
|
||||
$.ajax({url: requestUrl,
|
||||
dataType: "text",
|
||||
complete: function(jqxhr, textstatus) {
|
||||
var data = jqxhr.responseText;
|
||||
if (data !== '') {
|
||||
if (data !== '' && data !== undefined) {
|
||||
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
|
||||
}
|
||||
Search.output.append(listItem);
|
||||
|
@ -474,6 +317,7 @@ var Search = {
|
|||
*/
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var docnames = this._index.docnames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
@ -484,12 +328,13 @@ var Search = {
|
|||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var fullnameLower = fullname.toLowerCase()
|
||||
if (fullnameLower.indexOf(object) > -1) {
|
||||
var score = 0;
|
||||
var parts = fullname.split('.');
|
||||
var parts = fullnameLower.split('.');
|
||||
// check for different match types: exact matches of full name or
|
||||
// "last name" (i.e. last dotted part)
|
||||
if (fullname == object || parts[parts.length - 1] == object) {
|
||||
if (fullnameLower == object || parts[parts.length - 1] == object) {
|
||||
score += Scorer.objNameMatch;
|
||||
// matches in last name
|
||||
} else if (parts[parts.length - 1].indexOf(object) > -1) {
|
||||
|
@ -527,7 +372,7 @@ var Search = {
|
|||
} else {
|
||||
score += Scorer.objPrioDefault;
|
||||
}
|
||||
results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
|
||||
results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,27 +383,65 @@ var Search = {
|
|||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch : function(searchterms, excluded, terms, score) {
|
||||
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||
var docnames = this._index.docnames;
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i, j, file, files;
|
||||
var i, j, file;
|
||||
var fileMap = {};
|
||||
var scoreMap = {};
|
||||
var results = [];
|
||||
|
||||
// perform the search on the required terms
|
||||
for (i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
// no match but word was a required one
|
||||
if ((files = terms[word]) === undefined)
|
||||
break;
|
||||
if (files.length === undefined) {
|
||||
files = [files];
|
||||
var files = [];
|
||||
var _o = [
|
||||
{files: terms[word], score: Scorer.term},
|
||||
{files: titleterms[word], score: Scorer.title}
|
||||
];
|
||||
// add support for partial matches
|
||||
if (word.length > 2) {
|
||||
for (var w in terms) {
|
||||
if (w.match(word) && !terms[word]) {
|
||||
_o.push({files: terms[w], score: Scorer.partialTerm})
|
||||
}
|
||||
}
|
||||
for (var w in titleterms) {
|
||||
if (w.match(word) && !titleterms[word]) {
|
||||
_o.push({files: titleterms[w], score: Scorer.partialTitle})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no match but word was a required one
|
||||
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||
break;
|
||||
}
|
||||
// found search word in contents
|
||||
$u.each(_o, function(o) {
|
||||
var _files = o.files;
|
||||
if (_files === undefined)
|
||||
return
|
||||
|
||||
if (_files.length === undefined)
|
||||
_files = [_files];
|
||||
files = files.concat(_files);
|
||||
|
||||
// set score for the word in each file to Scorer.term
|
||||
for (j = 0; j < _files.length; j++) {
|
||||
file = _files[j];
|
||||
if (!(file in scoreMap))
|
||||
scoreMap[file] = {};
|
||||
scoreMap[file][word] = o.score;
|
||||
}
|
||||
});
|
||||
|
||||
// create the mapping
|
||||
for (j = 0; j < files.length; j++) {
|
||||
file = files[j];
|
||||
if (file in fileMap)
|
||||
if (file in fileMap && fileMap[file].indexOf(word) === -1)
|
||||
fileMap[file].push(word);
|
||||
else
|
||||
fileMap[file] = [word];
|
||||
|
@ -570,13 +453,19 @@ var Search = {
|
|||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchterms.length)
|
||||
continue;
|
||||
var filteredTermCount = // as search terms with length < 3 are discarded: ignore
|
||||
searchterms.filter(function(term){return term.length > 2}).length
|
||||
if (
|
||||
fileMap[file].length != searchterms.length &&
|
||||
fileMap[file].length != filteredTermCount
|
||||
) continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
for (i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file)) {
|
||||
titleterms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file) ||
|
||||
$u.contains(titleterms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
@ -584,7 +473,10 @@ var Search = {
|
|||
|
||||
// if we have still a valid result we can add it to the result list
|
||||
if (valid) {
|
||||
results.push([filenames[file], titles[file], '', null, score]);
|
||||
// select one (max) score for the file.
|
||||
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
|
||||
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
|
||||
results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
@ -594,10 +486,11 @@ var Search = {
|
|||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* words. the first one is used to find the occurrence, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
makeSearchSummary : function(text, keywords, hlwords) {
|
||||
makeSearchSummary : function(htmlText, keywords, hlwords) {
|
||||
var text = Search.htmlToText(htmlText);
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
|
@ -619,4 +512,4 @@ var Search = {
|
|||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
||||
});
|
||||
|
|
999
_static/underscore-1.3.1.js
Normal file
999
_static/underscore-1.3.1.js
Normal file
|
@ -0,0 +1,999 @@
|
|||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
|
||||
// Baseline setup
|
||||
// --------------
|
||||
|
||||
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the `_` variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets returned to break out of a loop iteration.
|
||||
var breaker = {};
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||
|
||||
// All **ECMAScript 5** native function implementations that we hope to use
|
||||
// are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys,
|
||||
nativeBind = FuncProto.bind;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for **Node.js**, with
|
||||
// backwards-compatibility for the old `require()` API. If we're in
|
||||
// the browser, add `_` as a global object via a string identifier,
|
||||
// for Closure Compiler "advanced" mode.
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = _;
|
||||
}
|
||||
exports._ = _;
|
||||
} else {
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.3.1';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (_.has(obj, key)) {
|
||||
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||
_.map = _.collect = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
results[results.length] = iterator.call(context, value, index, list);
|
||||
});
|
||||
if (obj.length === +obj.length) results.length = obj.length;
|
||||
return results;
|
||||
};
|
||||
|
||||
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
}
|
||||
});
|
||||
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as `foldr`.
|
||||
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||
}
|
||||
var reversed = _.toArray(obj).reverse();
|
||||
if (context && !initial) iterator = _.bind(iterator, context);
|
||||
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||
_.find = _.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
any(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||
// Aliased as `select`.
|
||||
_.filter = _.select = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||
// Aliased as `any`.
|
||||
var any = _.some = _.any = function(obj, iterator, context) {
|
||||
iterator || (iterator = _.identity);
|
||||
var result = false;
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
// Aliased as `contains`.
|
||||
_.include = _.contains = function(obj, target) {
|
||||
var found = false;
|
||||
if (obj == null) return found;
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
found = any(obj, function(value) {
|
||||
return value === target;
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method (with arguments) on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of `map`: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum element or (element-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return Infinity;
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Shuffle an array.
|
||||
_.shuffle = function(obj) {
|
||||
var shuffled = [], rand;
|
||||
each(obj, function(value, index, list) {
|
||||
if (index == 0) {
|
||||
shuffled[0] = value;
|
||||
} else {
|
||||
rand = Math.floor(Math.random() * (index + 1));
|
||||
shuffled[index] = shuffled[rand];
|
||||
shuffled[rand] = value;
|
||||
}
|
||||
});
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion. Pass either a string attribute
|
||||
// to group by, or a function that returns the criterion.
|
||||
_.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Safely convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// Array Functions
|
||||
// ---------------
|
||||
|
||||
// Get the first element of an array. Passing **n** will return the first N
|
||||
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||
// with `_.map`.
|
||||
_.first = _.head = function(array, n, guard) {
|
||||
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the last entry of the array. Especcialy useful on
|
||||
// the arguments object. Passing **n** will return all the values in
|
||||
// the array, excluding the last N. The **guard** check allows it to work with
|
||||
// `_.map`.
|
||||
_.initial = function(array, n, guard) {
|
||||
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
||||
};
|
||||
|
||||
// Get the last element of an array. Passing **n** will return the last N
|
||||
// values in the array. The **guard** check allows it to work with `_.map`.
|
||||
_.last = function(array, n, guard) {
|
||||
if ((n != null) && !guard) {
|
||||
return slice.call(array, Math.max(array.length - n, 0));
|
||||
} else {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||
// Especially useful on the arguments object. Passing an **index** will return
|
||||
// the rest of the values in the array from that index onward. The **guard**
|
||||
// check allows it to work with `_.map`.
|
||||
_.rest = _.tail = function(array, index, guard) {
|
||||
return slice.call(array, (index == null) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array, shallow) {
|
||||
return _.reduce(array, function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
||||
memo[memo.length] = value;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
// Aliased as `unique`.
|
||||
_.uniq = _.unique = function(array, isSorted, iterator) {
|
||||
var initial = iterator ? _.map(array, iterator) : array;
|
||||
var result = [];
|
||||
_.reduce(initial, function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
|
||||
memo[memo.length] = el;
|
||||
result[result.length] = array[i];
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments, true));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and a number of other arrays.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array) {
|
||||
var rest = _.flatten(slice.call(arguments, 1));
|
||||
return _.filter(array, function(value){ return !_.include(rest, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = slice.call(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||
// we need this function. Return the position of the first occurrence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||
// If the array is large and already in sort order, pass `true`
|
||||
// for **isSorted** to use binary search.
|
||||
_.indexOf = function(array, item, isSorted) {
|
||||
if (array == null) return -1;
|
||||
var i, l;
|
||||
if (isSorted) {
|
||||
i = _.sortedIndex(array, item);
|
||||
return array[i] === item ? i : -1;
|
||||
}
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (array == null) return -1;
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python `range()` function. See
|
||||
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||
_.range = function(start, stop, step) {
|
||||
if (arguments.length <= 1) {
|
||||
stop = start || 0;
|
||||
start = 0;
|
||||
}
|
||||
step = arguments[2] || 1;
|
||||
|
||||
var len = Math.max(Math.ceil((stop - start) / step), 0);
|
||||
var idx = 0;
|
||||
var range = new Array(len);
|
||||
|
||||
while(idx < len) {
|
||||
range[idx++] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
// Function (ahem) Functions
|
||||
// ------------------
|
||||
|
||||
// Reusable constructor function for prototype setting.
|
||||
var ctor = function(){};
|
||||
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function bind(func, context) {
|
||||
var bound, args;
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
if (!_.isFunction(func)) throw new TypeError;
|
||||
args = slice.call(arguments, 2);
|
||||
return bound = function() {
|
||||
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
||||
ctor.prototype = func.prototype;
|
||||
var self = new ctor;
|
||||
var result = func.apply(self, args.concat(slice.call(arguments)));
|
||||
if (Object(result) === result) return result;
|
||||
return self;
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = slice.call(arguments, 1);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||
};
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
_.throttle = function(func, wait) {
|
||||
var context, args, timeout, throttling, more;
|
||||
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
||||
return function() {
|
||||
context = this; args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (more) func.apply(context, args);
|
||||
whenDone();
|
||||
};
|
||||
if (!timeout) timeout = setTimeout(later, wait);
|
||||
if (throttling) {
|
||||
more = true;
|
||||
} else {
|
||||
func.apply(context, args);
|
||||
}
|
||||
whenDone();
|
||||
throttling = true;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
_.debounce = function(func, wait) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will be executed at most one time, no matter how
|
||||
// often you call it. Useful for lazy initialization.
|
||||
_.once = function(func) {
|
||||
var ran = false, memo;
|
||||
return function() {
|
||||
if (ran) return memo;
|
||||
ran = true;
|
||||
return memo = func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(slice.call(arguments, 0));
|
||||
return wrapper.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = arguments;
|
||||
return function() {
|
||||
var args = arguments;
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
if (times <= 0) return func();
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
||||
var keys = [];
|
||||
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
if (!_.isObject(obj)) return obj;
|
||||
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in
|
||||
// order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Internal recursive comparison function.
|
||||
function eq(a, b, stack) {
|
||||
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
||||
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
||||
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
||||
// A strict comparison is necessary because `null == undefined`.
|
||||
if (a == null || b == null) return a === b;
|
||||
// Unwrap any wrapped objects.
|
||||
if (a._chain) a = a._wrapped;
|
||||
if (b._chain) b = b._wrapped;
|
||||
// Invoke a custom `isEqual` method if one is provided.
|
||||
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
||||
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
||||
// Compare `[[Class]]` names.
|
||||
var className = toString.call(a);
|
||||
if (className != toString.call(b)) return false;
|
||||
switch (className) {
|
||||
// Strings, numbers, dates, and booleans are compared by value.
|
||||
case '[object String]':
|
||||
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
||||
// equivalent to `new String("5")`.
|
||||
return a == String(b);
|
||||
case '[object Number]':
|
||||
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
||||
// other numeric values.
|
||||
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
||||
case '[object Date]':
|
||||
case '[object Boolean]':
|
||||
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
||||
// millisecond representations. Note that invalid dates with millisecond representations
|
||||
// of `NaN` are not equivalent.
|
||||
return +a == +b;
|
||||
// RegExps are compared by their source patterns and flags.
|
||||
case '[object RegExp]':
|
||||
return a.source == b.source &&
|
||||
a.global == b.global &&
|
||||
a.multiline == b.multiline &&
|
||||
a.ignoreCase == b.ignoreCase;
|
||||
}
|
||||
if (typeof a != 'object' || typeof b != 'object') return false;
|
||||
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
||||
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
||||
var length = stack.length;
|
||||
while (length--) {
|
||||
// Linear search. Performance is inversely proportional to the number of
|
||||
// unique nested structures.
|
||||
if (stack[length] == a) return true;
|
||||
}
|
||||
// Add the first object to the stack of traversed objects.
|
||||
stack.push(a);
|
||||
var size = 0, result = true;
|
||||
// Recursively compare objects and arrays.
|
||||
if (className == '[object Array]') {
|
||||
// Compare array lengths to determine if a deep comparison is necessary.
|
||||
size = a.length;
|
||||
result = size == b.length;
|
||||
if (result) {
|
||||
// Deep compare the contents, ignoring non-numeric properties.
|
||||
while (size--) {
|
||||
// Ensure commutative equality for sparse arrays.
|
||||
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Objects with different constructors are not equivalent.
|
||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
||||
// Deep compare objects.
|
||||
for (var key in a) {
|
||||
if (_.has(a, key)) {
|
||||
// Count the expected number of properties.
|
||||
size++;
|
||||
// Deep compare each member.
|
||||
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
||||
}
|
||||
}
|
||||
// Ensure that both objects contain the same number of properties.
|
||||
if (result) {
|
||||
for (key in b) {
|
||||
if (_.has(b, key) && !(size--)) break;
|
||||
}
|
||||
result = !size;
|
||||
}
|
||||
}
|
||||
// Remove the first object from the stack of traversed objects.
|
||||
stack.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
return eq(a, b, []);
|
||||
};
|
||||
|
||||
// Is a given array, string, or object empty?
|
||||
// An "empty" object has no enumerable own-properties.
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (_.has(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return toString.call(obj) == '[object Arguments]';
|
||||
};
|
||||
if (!_.isArguments(arguments)) {
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && _.has(obj, 'callee'));
|
||||
};
|
||||
}
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return toString.call(obj) == '[object Function]';
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return toString.call(obj) == '[object String]';
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return toString.call(obj) == '[object Number]';
|
||||
};
|
||||
|
||||
// Is the given value `NaN`?
|
||||
_.isNaN = function(obj) {
|
||||
// `NaN` is the only value for which `===` is not reflexive.
|
||||
return obj !== obj;
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return toString.call(obj) == '[object Date]';
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return toString.call(obj) == '[object RegExp]';
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return obj === void 0;
|
||||
};
|
||||
|
||||
// Has own property?
|
||||
_.has = function(obj, key) {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
// -----------------
|
||||
|
||||
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function **n** times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Escape a string for HTML interpolation.
|
||||
_.escape = function(string) {
|
||||
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
evaluate : /<%([\s\S]+?)%>/g,
|
||||
interpolate : /<%=([\s\S]+?)%>/g,
|
||||
escape : /<%-([\s\S]+?)%>/g
|
||||
};
|
||||
|
||||
// When customizing `templateSettings`, if you don't want to define an
|
||||
// interpolation, evaluation or escaping regex, we need one that is
|
||||
// guaranteed not to match.
|
||||
var noMatch = /.^/;
|
||||
|
||||
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
||||
// that had been previously added.
|
||||
var unescape = function(code) {
|
||||
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
|
||||
};
|
||||
|
||||
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||
// and correctly escapes quotes within interpolated code.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
'with(obj||{}){__p.push(\'' +
|
||||
str.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(c.escape || noMatch, function(match, code) {
|
||||
return "',_.escape(" + unescape(code) + "),'";
|
||||
})
|
||||
.replace(c.interpolate || noMatch, function(match, code) {
|
||||
return "'," + unescape(code) + ",'";
|
||||
})
|
||||
.replace(c.evaluate || noMatch, function(match, code) {
|
||||
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
|
||||
})
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
+ "');}return __p.join('');";
|
||||
var func = new Function('obj', '_', tmpl);
|
||||
if (data) return func(data, _);
|
||||
return function(data) {
|
||||
return func.call(this, data, _);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a "chain" function, which will delegate to the wrapper.
|
||||
_.chain = function(obj) {
|
||||
return _(obj).chain();
|
||||
};
|
||||
|
||||
// The OOP Wrapper
|
||||
// ---------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Expose `wrapper.prototype` as `_.prototype`
|
||||
_.prototype = wrapper.prototype;
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = slice.call(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
var wrapped = this._wrapped;
|
||||
method.apply(wrapped, arguments);
|
||||
var length = wrapped.length;
|
||||
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
||||
return result(wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
}).call(this);
|
2542
capnp.html
2542
capnp.html
File diff suppressed because it is too large
Load diff
944
genindex.html
944
genindex.html
File diff suppressed because it is too large
Load diff
151
index.html
151
index.html
|
@ -1,33 +1,23 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Welcome to pycapnp’s documentation! — capnp 0.5.4 documentation</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pycapnp — capnp 1.0.0b2 documentation</title>
|
||||
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.5.4',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="capnp 0.5.4 documentation" href="#" />
|
||||
<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="Installation" href="install.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
</head><body>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -39,38 +29,43 @@
|
|||
<li class="right" >
|
||||
<a href="install.html" title="Installation"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li><a href="#">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">pycapnp</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="welcome-to-pycapnp-s-documentation">
|
||||
<h1>Welcome to pycapnp’s documentation!<a class="headerlink" href="#welcome-to-pycapnp-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This is a python wrapping of the C++ implementation of the <a class="reference external" href="http://kentonv.github.io/capnproto/">Cap’n Proto</a> library. Here is a short description, quoted from its docs:</p>
|
||||
<div class="section" id="pycapnp">
|
||||
<h1>pycapnp<a class="headerlink" href="#pycapnp" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This is a python wrapping of the C++ implementation of the <a class="reference external" href="https://capnproto.org/">Cap’n Proto</a> library. Here is a short description, quoted from its docs:</p>
|
||||
<blockquote>
|
||||
<div>Cap’n Proto is an insanely fast data interchange format and capability-based RPC system. Think JSON, except binary. Or think Protocol Buffers, except faster. In fact, in benchmarks, Cap’n Proto is INFINITY TIMES faster than Protocol Buffers.</div></blockquote>
|
||||
<p>Since the python library is just a thin wrapping of the C++ library, we inherit a lot of what makes Cap’n Proto fast. In some simplistic benchmarks (available in the <a class="reference external" href="https://github.com/jparyani/pycapnp/tree/master/benchmark">benchmark directory of the repo</a>), pycapnp has proven to be decently faster than Protocol Buffers (both pure python and C++ implementations). Also, the python capnp library can load Cap’n Proto schema files directly, without the need for a seperate compile step like with Protocol Buffers or Thrift. pycapnp is available on <a class="reference external" href="https://github.com/jparyani/pycapnp.git">github</a> and <a class="reference external" href="https://pypi.python.org/pypi/pycapnp">pypi</a>.</p>
|
||||
<div><p>Cap’n Proto is an insanely fast data interchange format and capability-based RPC system. Think JSON, except binary. Or think Protocol Buffers, except faster. In fact, in benchmarks, Cap’n Proto is INFINITY TIMES faster than Protocol Buffers.</p>
|
||||
</div></blockquote>
|
||||
<p>Since the python library is just a thin wrapping of the C++ library, we inherit a lot of what makes Cap’n Proto fast. In some simplistic benchmarks (available in the <a class="reference external" href="https://github.com/capnproto/pycapnp/tree/master/benchmark">benchmark directory of the repo</a>), pycapnp has proven to be decently faster than Protocol Buffers (both pure python and C++ implementations). Also, the python capnp library can load Cap’n Proto schema files directly, without the need for a seperate compile step like with Protocol Buffers or Thrift. pycapnp is available on <a class="reference external" href="https://github.com/capnproto/pycapnp.git">github</a> and <a class="reference external" href="https://pypi.python.org/pypi/pycapnp">pypi</a>.</p>
|
||||
<p>Contents:</p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Installation</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#c-cap-n-proto-library">C++ Cap’n Proto Library</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#pip">Pip</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#from-source">From Source</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#from-source">From Source</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#c-cap-n-proto-library">C++ Cap’n Proto Library</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="install.html#pycapnp-from-git">pycapnp from git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="install.html#development">Development</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="quickstart.html#load-a-cap-n-proto-schema">Load a Cap’n Proto Schema</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="quickstart.html#load-a-cap-n-proto-schema">Load a Cap’n Proto Schema</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#const-values">Const values</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="quickstart.html#build-a-message">Build a message</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#initialize-a-new-cap-n-proto-object">Initialize a New Cap’n Proto Object</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#initialize-a-new-cap-n-proto-object">Initialize a New Cap’n Proto Object</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#list">List</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#primitive-types">Primitive Types</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#enums">Enums</a></li>
|
||||
|
@ -89,20 +84,22 @@
|
|||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#multi-message-files">Multi-message files</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#dictionaries">Dictionaries</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#byte-strings-buffers">Byte Strings/Buffers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#byte-segments">Byte Segments</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="quickstart.html#rpc">RPC</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#client">Client</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#starting-a-client">Starting a Client</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#restoring">Restoring</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#starting-a-client-asyncio">Starting a Client (asyncio)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#bootstrap">Bootstrap</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#calling-methods">Calling methods</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#pipelining">Pipelining</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="quickstart.html#server">Server</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#starting-a-server">Starting a Server</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#starting-a-server-asyncio">Starting a Server (asyncio)</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#implementing-a-server">Implementing a Server</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="quickstart.html#restore">Restore</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -111,72 +108,71 @@
|
|||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="capnp.html">API Reference</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="capnp.html#internal-classes">Internal Classes</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#modules">Modules</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#readers">Readers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#builders">Builders</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#rpc">RPC</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="capnp.html#classes">Classes</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#rpc">RPC</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="capnp.html#promise">Promise</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="capnp.html#communication">Communication</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="capnp.html#capability">Capability</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="capnp.html#response">Response</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#miscellaneous">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="capnp.html#functions">Functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="capnp.html#classes">Classes</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#id3">RPC</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#id4">Miscellaneous</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="capnp.html#internal-classes">Internal Classes</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#modules">Modules</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#readers">Readers</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#builders">Builders</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#id1">RPC</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="capnp.html#id2">Miscellaneous</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="indices-and-tables">
|
||||
<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1>
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="genindex.html"><em>Index</em></a></li>
|
||||
<li><a class="reference internal" href="py-modindex.html"><em>Module Index</em></a></li>
|
||||
<li><a class="reference internal" href="search.html"><em>Search Page</em></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="#">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Welcome to pycapnp’s documentation!</a></li>
|
||||
<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li>
|
||||
<h3><a href="#">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="capnp.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="install.html"
|
||||
title="next chapter">Installation</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/index.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/index.rst.txt"
|
||||
rel="nofollow">Show Source</a></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" />
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -188,12 +184,13 @@
|
|||
<li class="right" >
|
||||
<a href="install.html" title="Installation"
|
||||
>next</a> |</li>
|
||||
<li><a href="#">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="#">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">pycapnp</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Author.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander).
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.1.0.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
214
install.html
214
install.html
|
@ -1,34 +1,24 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Installation — capnp 0.5.4 documentation</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Installation — capnp 1.0.0b2 documentation</title>
|
||||
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.5.4',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="capnp 0.5.4 documentation" href="index.html" />
|
||||
<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="Quickstart" href="quickstart.html" />
|
||||
<link rel="prev" title="Welcome to pycapnp’s documentation!" href="index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<link rel="prev" title="pycapnp" href="index.html" />
|
||||
</head><body>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -41,120 +31,167 @@
|
|||
<a href="quickstart.html" title="Quickstart"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Welcome to pycapnp’s documentation!"
|
||||
<a href="index.html" title="pycapnp"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Installation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="installation">
|
||||
<span id="install"></span><h1>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="c-cap-n-proto-library">
|
||||
<h2>C++ Cap’n Proto Library<a class="headerlink" href="#c-cap-n-proto-library" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You need to install the C++ Cap’n Proto library first. It requires a C++ compiler with C++11 support, such as GCC 4.7+ or Clang 3.2+. Follow installation docs at <a class="reference external" href="http://kentonv.github.io/capnproto/install.html">http://kentonv.github.io/capnproto/install.html</a>, or if you’re feeling lazy, you can run the commands below:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>curl -O http://capnproto.org/capnproto-c++-0.5.0.tar.gz
|
||||
tar zxf capnproto-c++-0.5.0.tar.gz
|
||||
cd capnproto-c++-0.5.0
|
||||
./configure
|
||||
make -j6 check
|
||||
sudo make install
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pip">
|
||||
<h2>Pip<a class="headerlink" href="#pip" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Using pip is by far the easiest way to install the library. After you’ve installed the C++ library, all you need to run is:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>[sudo] pip install -U cython
|
||||
[sudo] pip install -U setuptools
|
||||
[sudo] pip install pycapnp
|
||||
<p>The pip installation will using the binary versions of the package (if possible). These contain a bundled version of capnproto (on Linux compiled with <a class="reference external" href="https://github.com/pypa/manylinux">manylinux</a>). Starting from v1.0.0b1 binary releases are available for Windows, macOS and Linux from <a class="reference external" href="https://pypi.org/project/pycapnp/#history">pypi</a>:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">sudo</span><span class="p">]</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">pycapnp</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>On some systems you will have to install Python’s headers before doing any of this. For Debian/Ubuntu, this is:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>sudo apt-get install python-dev
|
||||
<p>To force rebuilding the pip package from source (you’ll need requirments.txt or pipenv):</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">binary</span> <span class="p">:</span><span class="nb">all</span><span class="p">:</span> <span class="n">pycapnp</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To force bundling libcapnp (or force system libcapnp), just in case setup.py isn’t doing the right thing:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">binary</span> <span class="p">:</span><span class="nb">all</span><span class="p">:</span> <span class="o">--</span><span class="n">install</span><span class="o">-</span><span class="n">option</span> <span class="s2">"--force-bundled-libcapnp"</span>
|
||||
<span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">binary</span> <span class="p">:</span><span class="nb">all</span><span class="p">:</span> <span class="o">--</span><span class="n">install</span><span class="o">-</span><span class="n">option</span> <span class="s2">"--force-system-libcapnp"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If you’re using an older Linux distro (e.g. CentOS 6) you many need to set <cite>LDFLAGS=”-Wl,–no-as-needed -lrt”</cite>:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">LDFLAGS</span><span class="o">=</span><span class="s2">"-Wl,--no-as-needed -lrt"</span> <span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">binary</span> <span class="p">:</span><span class="nb">all</span><span class="p">:</span> <span class="n">pycapnp</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>It’s also possible to specify the libcapnp url when bundling (this may not work, there be dragons):</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">--</span><span class="n">no</span><span class="o">-</span><span class="n">binary</span> <span class="p">:</span><span class="nb">all</span><span class="p">:</span> <span class="o">--</span><span class="n">install</span><span class="o">-</span><span class="n">option</span> <span class="s2">"--force-bundled-libcapnp"</span> <span class="o">--</span><span class="n">install</span><span class="o">-</span><span class="n">option</span> <span class="s2">"--libcapnp-url"</span> <span class="o">--</span><span class="n">install</span><span class="o">-</span><span class="n">option</span> <span class="s2">"https://github.com/capnproto/capnproto/archive/master.tar.gz"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>You can control what compiler is used with the environment variable CC, ie. <cite>CC=gcc-4.8 pip install pycapnp</cite>, and flags with CFLAGS. You only need to run the setuptools line if you have a setuptools older than v0.8.0, and the cython line if you have a version older than v0.19.1.</p>
|
||||
</div>
|
||||
<div class="section" id="from-source">
|
||||
<h2>From Source<a class="headerlink" href="#from-source" title="Permalink to this headline">¶</a></h2>
|
||||
<p>If you want the latest development version, you can clone the github repo and install like so:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>git clone https://github.com/jparyani/pycapnp.git
|
||||
pip install ./pycapnp
|
||||
<p>Source installation is generally not needed unless you’re looking into an issue with capnproto or pycapnp itself.</p>
|
||||
<div class="section" id="c-cap-n-proto-library">
|
||||
<h3>C++ Cap’n Proto Library<a class="headerlink" href="#c-cap-n-proto-library" title="Permalink to this headline">¶</a></h3>
|
||||
<p>You need to install the C++ Cap’n Proto library first. It requires a C++ compiler with C++14 support, such as GCC 5+ or Clang 5+. Follow installation docs at <a class="reference external" href="https://capnproto.org/install.html">https://capnproto.org/install.html</a>.</p>
|
||||
</div>
|
||||
<div class="section" id="pycapnp-from-git">
|
||||
<h3>pycapnp from git<a class="headerlink" href="#pycapnp-from-git" title="Permalink to this headline">¶</a></h3>
|
||||
<p>If you want the latest development version, you can clone the github repo:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">capnproto</span><span class="o">/</span><span class="n">pycapnp</span><span class="o">.</span><span class="n">git</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>For development packages use one of the following to install the python dependencies:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pipenv</span> <span class="n">install</span>
|
||||
<span class="n">pip</span> <span class="n">install</span> <span class="o">-</span><span class="n">r</span> <span class="n">requirements</span><span class="o">.</span><span class="n">txt</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>And install pycapnp with:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="n">pycapnp</span>
|
||||
<span class="n">pip</span> <span class="n">install</span> <span class="o">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>cd pycapnp
|
||||
python setup.py install
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="n">pycapnp</span>
|
||||
<span class="n">python</span> <span class="n">setup</span><span class="o">.</span><span class="n">py</span> <span class="n">install</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="development">
|
||||
<h2>Development<a class="headerlink" href="#development" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Clone the repo from <a class="reference external" href="https://github.com/jparyani/pycapnp.git">https://github.com/jparyani/pycapnp.git</a> and use the <cite>develop</cite> branch. I’ll probably ask you to redo pull requests that target <cite>master</cite> and aren’t easily mergable to <cite>develop</cite>:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>git clone https://github.com/jparyani/pycapnp.git
|
||||
git checkout develop
|
||||
<p>Clone the repo from <a class="reference external" href="https://github.com/capnproto/pycapnp.git">https://github.com/capnproto/pycapnp.git</a>:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">capnproto</span><span class="o">/</span><span class="n">pycapnp</span><span class="o">.</span><span class="n">git</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Testing is done through pytest, like so:</p>
|
||||
<div class="highlight-python"><div class="highlight"><pre>pip install pytest
|
||||
py.test
|
||||
<p>For development packages use one of the following to install the python dependencies:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pipenv</span> <span class="n">install</span>
|
||||
<span class="n">pip</span> <span class="n">install</span> <span class="o">-</span><span class="n">r</span> <span class="n">requirements</span><span class="o">.</span><span class="n">txt</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Once you’re done installing, take a look at the <a class="reference internal" href="quickstart.html#quickstart"><em>Quickstart</em></a></p>
|
||||
<p>Building:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="n">pycapnp</span>
|
||||
<span class="n">pip</span> <span class="n">install</span> <span class="o">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="n">pycapnp</span>
|
||||
<span class="n">python</span> <span class="n">setup</span><span class="o">.</span><span class="n">py</span> <span class="n">install</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Useful targets for setup.py:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">python</span> <span class="n">setup</span><span class="o">.</span><span class="n">py</span> <span class="n">build</span>
|
||||
<span class="n">python</span> <span class="n">setup</span><span class="o">.</span><span class="n">py</span> <span class="n">clean</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Useful command-line arguments are available for setup.py:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">--</span><span class="n">force</span><span class="o">-</span><span class="n">bundled</span><span class="o">-</span><span class="n">libcapnp</span>
|
||||
<span class="o">--</span><span class="n">force</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">libcapnp</span>
|
||||
<span class="o">--</span><span class="n">libcapnp</span><span class="o">-</span><span class="n">url</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Testing is done through pytest:</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">cd</span> <span class="n">pycapnp</span>
|
||||
<span class="n">pytest</span>
|
||||
<span class="n">pytest</span> <span class="n">test</span><span class="o">/</span><span class="n">test_rpc_calculator</span><span class="o">.</span><span class="n">py</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Once you’re done installing, take a look at the <a class="reference internal" href="quickstart.html#quickstart"><span class="std std-ref">Quickstart</span></a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Installation</a><ul>
|
||||
<li><a class="reference internal" href="#c-cap-n-proto-library">C++ Cap’n Proto Library</a></li>
|
||||
<li><a class="reference internal" href="#pip">Pip</a></li>
|
||||
<li><a class="reference internal" href="#from-source">From Source</a></li>
|
||||
<li><a class="reference internal" href="#development">Development</a></li>
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Installation</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#pip">Pip</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#from-source">From Source</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#c-cap-n-proto-library">C++ Cap’n Proto Library</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#pycapnp-from-git">pycapnp from git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#development">Development</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="capnp.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="index.html"
|
||||
title="previous chapter">Welcome to pycapnp’s documentation!</a></p>
|
||||
title="previous chapter">pycapnp</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="quickstart.html"
|
||||
title="next chapter">Quickstart</a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/install.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/install.rst.txt"
|
||||
rel="nofollow">Show Source</a></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" />
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -167,14 +204,15 @@ py.test
|
|||
<a href="quickstart.html" title="Quickstart"
|
||||
>next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="index.html" title="Welcome to pycapnp’s documentation!"
|
||||
<a href="index.html" title="pycapnp"
|
||||
>previous</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Installation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Author.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander).
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.1.0.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
BIN
objects.inv
BIN
objects.inv
Binary file not shown.
|
@ -1,39 +1,29 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Python Module Index — capnp 0.5.4 documentation</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Python Module Index — capnp 1.0.0b2 documentation</title>
|
||||
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.5.4',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="top" title="capnp 0.5.4 documentation" href="index.html" />
|
||||
<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" />
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
DOCUMENTATION_OPTIONS.COLLAPSE_INDEX = true;
|
||||
</script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
</head><body>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -42,14 +32,15 @@
|
|||
<li class="right" >
|
||||
<a href="#" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Python Module Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
<div class="body" role="main">
|
||||
|
||||
|
||||
<h1>Python Module Index</h1>
|
||||
|
@ -58,41 +49,46 @@
|
|||
<a href="#cap-c"><strong>c</strong></a>
|
||||
</div>
|
||||
|
||||
<table class="indextable modindextable" cellspacing="0" cellpadding="2">
|
||||
<tr class="pcap"><td></td><td> </td><td></td></tr>
|
||||
<table class="indextable modindextable">
|
||||
<tr class="pcap"><td></td><td> </td><td></td></tr>
|
||||
<tr class="cap" id="cap-c"><td></td><td>
|
||||
<strong>c</strong></td><td></td></tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<a href="capnp.html#module-capnp"><tt class="xref">capnp</tt></a></td><td>
|
||||
<a href="capnp.html#module-capnp"><code class="xref">capnp</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<div id="searchbox" style="display: none">
|
||||
<h3>Quick search</h3>
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="capnp.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<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" />
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
<p class="searchtip" style="font-size: 90%">
|
||||
Enter search terms or a module, class or function name.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -101,12 +97,13 @@
|
|||
<li class="right" >
|
||||
<a href="#" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Python Module Index</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Author.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander).
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.1.0.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
821
quickstart.html
821
quickstart.html
File diff suppressed because it is too large
Load diff
81
search.html
81
search.html
|
@ -1,40 +1,27 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Search — capnp 0.5.4 documentation</title>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Search — capnp 1.0.0b2 documentation</title>
|
||||
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.5.4',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
<link rel="top" title="capnp 0.5.4 documentation" href="index.html" />
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" id="searchindexloader"></script>
|
||||
<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>
|
||||
<script src="_static/searchtools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
<script src="searchindex.js" defer></script>
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
</head><body>
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -43,31 +30,30 @@
|
|||
<li class="right" >
|
||||
<a href="py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
<div class="body" role="main">
|
||||
|
||||
<h1 id="search-documentation">Search</h1>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<script>$('#fallback').hide();</script>
|
||||
<p>
|
||||
Please activate JavaScript to enable the search
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.
|
||||
Searching for multiple words only shows matches that contain
|
||||
all words.
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="text" name="q" aria-labelledby="search-documentation" value="" />
|
||||
<input type="submit" value="search" />
|
||||
<span id="search-progress" style="padding-left: 10px"></span>
|
||||
</form>
|
||||
|
@ -76,16 +62,24 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table of Contents</a></h3>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="install.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="capnp.html">API Reference</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<div class="related" role="navigation" aria-label="related navigation">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
|
@ -94,12 +88,13 @@
|
|||
<li class="right" >
|
||||
<a href="py-modindex.html" title="Python Module Index"
|
||||
>modules</a> |</li>
|
||||
<li><a href="index.html">capnp 0.5.4 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-0"><a href="index.html">capnp 1.0.0b2 documentation</a> »</li>
|
||||
<li class="nav-item nav-item-this"><a href="">Search</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2013, Author.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.3.
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander).
|
||||
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.1.0.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue