Skip to content

Commit c8a475b

Browse files
committed
Update docs some more.
* Add API docs for protocol parsing, including the receiver interface. * Change a bunch of fixed-width text to links. * Update the docstrings in the parsley module.
1 parent acb499e commit c8a475b

File tree

3 files changed

+125
-49
lines changed

3 files changed

+125
-49
lines changed

doc/reference.rst

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,74 @@ Python API
7474
:members:
7575

7676

77+
Protocol parsing API
78+
====================
79+
80+
.. py:module:: ometa.protocol
81+
82+
.. py:class:: ParserProtocol
83+
84+
The Twisted ``Protocol`` subclass used for :ref:`parsing stream protocols
85+
using Parsley <protocol-parsing>`. It has two public attributes:
86+
87+
.. py:attribute:: sender
88+
89+
After the connection is established, this attribute will refer to the
90+
sender created by the sender factory of the :class:`ParserProtocol`.
91+
92+
.. py:attribute:: receiver
93+
94+
After the connection is established, this attribute will refer to the
95+
receiver created by the receiver factory of the :class:`ParserProtocol`.
96+
97+
It's common to also add a ``factory`` attribute to the
98+
:class:`ParserProtocol` from its factory's ``buildProtocol`` method, but
99+
this isn't strictly required or guaranteed to be present.
100+
101+
Subclassing or instantiating :class:`ParserProtocol` is not necessary;
102+
:func:`~parsley.makeProtocol` is sufficient and requires less boilerplate.
103+
104+
.. _receivers:
105+
106+
.. py:class:: Receiver
107+
108+
:class:`Receiver` is not a real class but is used here for demonstration
109+
purposes to indicate the required API.
110+
111+
.. py:attribute:: currentRule
112+
113+
:class:`ParserProtocol` examines the :attr:`currentRule` attribute at the
114+
beginning of parsing as well as after every time a rule has completely
115+
matched. At these times, the rule with the same name as the value of
116+
:attr:`currentRule` will be selected to start parsing the incoming stream
117+
of data.
118+
119+
.. py:method:: prepareParsing(parserProtocol)
120+
121+
:meth:`prepareParsing` is called after the :class:`.ParserProtocol` has
122+
established a connection, and is passed the :class:`.ParserProtocol`
123+
instance itself.
124+
125+
:param parserProtocol: An instance of :class:`.ProtocolParser`.
126+
127+
.. py:method:: finishParsing(reason)
128+
129+
:meth:`finishParsing` is called if an exception was raised during
130+
parsing, or when the :class:`.ParserProtocol` has lost its connection,
131+
whichever comes first. It will only be called once.
132+
133+
An exception raised during parsing can be due to incoming data that
134+
doesn't match the current rule or an exception raised calling python code
135+
during matching.
136+
137+
:param reason: A `Failure`_ encapsulating the reason parsing has ended.
138+
139+
.. _senders:
140+
141+
Senders do not have any required API as :class:`ParserProtocol` will never call
142+
methods on a sender.
143+
144+
77145
Built-in Parsley Rules
78146
~~~~~~~~~~~~~~~~~~~~~~
79147

@@ -97,3 +165,6 @@ Built-in Parsley Rules
97165

98166
``exactly(char)``:
99167
Matches the character `char`.
168+
169+
170+
.. _Failure: http://twistedmatrix.com/documents/current/api/twisted.python.failure.Failure.html

doc/tutorial3.rst

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _protocol-parsing:
2+
13
===============================================
24
Parsley Tutorial Part III: Parsing Network Data
35
===============================================
@@ -16,9 +18,8 @@ framing. Fortunately, Parsley can remove all of this tedium.
1618

1719
With :func:`parsley.makeProtocol`, Parsley can generate a `Twisted`_
1820
`IProtocol`_-implementing class which will match incoming network data using
19-
Parsley grammar rules. Before getting started with
20-
:func:`~parsley.makeProtocol`, let's build a grammar for `netstrings`_. The
21-
netstrings protocol is very simple::
21+
Parsley grammar rules. Before getting started with :func:`.makeProtocol`, let's
22+
build a grammar for `netstrings`_. The netstrings protocol is very simple::
2223

2324
4:spam,4:eggs,
2425

@@ -30,9 +31,9 @@ prefixed with one or more ASCII digits followed by a ``:``, and suffixed with a
3031
:start-after: grammar =
3132
:end-before: receiveNetstring
3233

33-
:func:`~parsley.makeProtocol` takes, in addition to a grammar, a factory for a
34-
"sender" and a factory for a "receiver". In the system of objects managed by
35-
the ``ParserProtocol``, the sender is in charge of writing data to the wire,
34+
:func:`.makeProtocol` takes, in addition to a grammar, a factory for a "sender"
35+
and a factory for a "receiver". In the system of objects managed by the
36+
:class:`.ParserProtocol`, the sender is in charge of writing data to the wire,
3637
and the receiver has methods called on it by the Parsley rules. To demonstrate
3738
it, here is the final piece needed in the Parsley grammar for netstrings:
3839

@@ -43,44 +44,45 @@ it, here is the final piece needed in the Parsley grammar for netstrings:
4344
The receiver is always available in Parsley rules with the name ``receiver``,
4445
allowing Parsley rules to call methods on it.
4546

46-
When data is received over the wire, the ``ParserProtocol`` tries to match the
47-
received data against the current rule. If the current rule requires more data
48-
to finish matching, the ``ParserProtocol`` stops and waits until more data
49-
comes in, then tries to continue matching. This repeats until the current rule
50-
is completely matched, and then the ``ParserProtocol`` starts matching any
51-
leftover data against the current rule again.
47+
When data is received over the wire, the :class:`.ParserProtocol` tries to
48+
match the received data against the current rule. If the current rule requires
49+
more data to finish matching, the :class:`.ParserProtocol` stops and waits
50+
until more data comes in, then tries to continue matching. This repeats until
51+
the current rule is completely matched, and then the :class:`.ParserProtocol`
52+
starts matching any leftover data against the current rule again.
5253

53-
One specifies the current rule by setting a ``currentRule`` attribute on the
54-
receiver, which the ``ParserProtocol`` looks at before doing any parsing.
55-
Changing the current rule is addressed in the :ref:`Switching rules
54+
One specifies the current rule by setting a :attr:`.currentRule` attribute on
55+
the receiver, which the :class:`.ParserProtocol` looks at before doing any
56+
parsing. Changing the current rule is addressed in the :ref:`Switching rules
5657
<switching-rules>` section.
5758

58-
Since the ``ParserProtocol`` will never modify the ``currentRule`` attribute
59-
itself, the default behavior is to keep using the same rule. Parsing netstrings
60-
doesn't require any rule changing, so, the default behavior of continuing to
61-
use the same rule is fine.
59+
Since the :class:`.ParserProtocol` will never modify the :attr:`.currentRule`
60+
attribute itself, the default behavior is to keep using the same rule. Parsing
61+
netstrings doesn't require any rule changing, so, the default behavior of
62+
continuing to use the same rule is fine.
6263

6364
Both the sender factory and receiver factory are constructed when the
64-
``ParserProtocol``'s connection is established. The sender factory is a
65-
one-argument callable which will be passed the ``ParserProtocol``'s
65+
:class:`.ParserProtocol`'s connection is established. The sender factory is a
66+
one-argument callable which will be passed the :class:`.ParserProtocol`'s
6667
`Transport`_. This allows the sender to send data over the transport. For
6768
example:
6869

6970
.. literalinclude:: _static/listings/tutorial3-netstrings.py
7071
:pyobject: NetstringSender
7172

7273
The receiver factory is another one-argument callable which is passed the
73-
constructed sender. The returned object must at least have ``prepareParsing``
74-
and ``finishParsing`` methods. ``prepareParsing`` is called with the
75-
``ParserProtocol`` instance when a connection is established (i.e. in the
76-
``connectionMade`` of the ``ParserProtocol``) and ``finishParsing`` is called
77-
when a connection is closed (i.e. in the ``connectionLost`` of the
78-
``ParserProtocol``).
74+
constructed sender. The returned object must at least have
75+
:meth:`.prepareParsing` and :meth:`.finishParsing` methods.
76+
:meth:`.prepareParsing` is called with the :class:`.ParserProtocol` instance
77+
when a connection is established (i.e. in the ``connectionMade`` of the
78+
:class:`.ParserProtocol`) and :meth:`.finishParsing` is called when a
79+
connection is closed (i.e. in the ``connectionLost`` of the
80+
:class:`.ParserProtocol`).
7981

8082
.. note::
81-
Both the receiver factory and its returned object's ``prepareParsing`` are
82-
called at in the ``ParserProtocol``'s ``connectionMade`` method; this
83-
separation is for ease of testing receivers.
83+
Both the receiver factory and its returned object's :meth:`.prepareParsing`
84+
are called at in the :class:`.ParserProtocol`'s ``connectionMade`` method;
85+
this separation is for ease of testing receivers.
8486

8587
To demonstrate a receiver, here is a simple receiver that receives netstrings
8688
and echos the same netstrings back:
@@ -105,8 +107,8 @@ Intermezzo: error reporting
105107
If an exception is raised from within Parsley during parsing, whether it's due
106108
to input not matching the current rule or an exception being raised from code
107109
the grammar calls, the connection will be immediately closed. The traceback
108-
will be captured as a `Failure`_ and passed to the ``finishParsing`` method of
109-
the receiver.
110+
will be captured as a `Failure`_ and passed to the :meth:`.finishParsing`
111+
method of the receiver.
110112

111113
At present, there is no way to recover from failure.
112114

@@ -116,8 +118,8 @@ Composing senders and receivers
116118

117119
The design of senders and receivers is intentional to make composition easy: no
118120
subclassing is required. While the composition is easy enough to do on your
119-
own, Parsley provides a function: :func:`~parsley.stack`. It takes a base
120-
factory followed by zero or more wrappers.
121+
own, Parsley provides a function: :func:`.stack`. It takes a base factory
122+
followed by zero or more wrappers.
121123

122124
Its use is extremely simple: ``stack(x, y, z)`` will return a callable suitable
123125
either as a sender or receiver factory which will, when called with an
@@ -172,18 +174,18 @@ data length and the data. The amended grammar would look something like this:
172174
:start-after: grammar =
173175
:end-before: """
174176

175-
Changing the current rule is as simple as changing the ``currentRule``
177+
Changing the current rule is as simple as changing the :attr:`.currentRule`
176178
attribute on the receiver. So, the ``netstringReceived`` method could look like
177179
this:
178180

179181
.. literalinclude:: _static/listings/tutorial3-netstrings2.py
180182
:pyobject: NetstringReceiver.netstringReceived
181183

182-
While changing the ``currentRule`` attribute can be done at any time, the
183-
``ParserProtocol`` only examines the ``currentRule`` at the beginning of
184-
parsing and after a rule has finished matching. As a result, if the
185-
``currentRule`` changes, the ``ParserProtocol`` will wait until the current
186-
rule is completely matched before switching rules.
184+
While changing the :attr:`.currentRule` attribute can be done at any time, the
185+
:class:`.ParserProtocol` only examines the :attr:`.currentRule` at the
186+
beginning of parsing and after a rule has finished matching. As a result, if
187+
the :attr:`.currentRule` changes, the :class:`.ParserProtocol` will wait until
188+
the current rule is completely matched before switching rules.
187189

188190
:download:`The complete script is also available for download.
189191
<_static/listings/tutorial3-netstrings2.py>`

parsley.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,19 @@ def invokeRule(*args, **kwargs):
102102
def makeProtocol(source, senderFactory, receiverFactory, bindings=None,
103103
name='Grammar'):
104104
"""
105-
Create a Protocol implementation from a Parsley grammar.
105+
Create a Twisted ``Protocol`` factory from a Parsley grammar.
106106
107107
:param source: A grammar, as a string.
108108
:param senderFactory: A one-argument callable that takes a twisted
109-
``Transport`` and returns a sender.
110-
:param receiverFactory: A two-argument callable that takes the sender
111-
returned by the ``senderFactory`` and the ``ParserProtocol`` instance and
112-
returns a receiver.
113-
:param bindings: A mapping of variable names to objects.
114-
:param name: Name used for the generated class.
109+
``Transport`` and returns a :ref:`sender <senders>`.
110+
:param receiverFactory: A one-argument callable that takes the sender
111+
returned by the ``senderFactory`` and returns a :ref:`receiver
112+
<receivers>`.
113+
:param bindings: A mapping of variable names to objects which will be
114+
accessible from python code in the grammar.
115+
:param name: The name used for the generated grammar class.
116+
:returns: A nullary callable which will return an instance of
117+
:class:`~.ParserProtocol`.
115118
"""
116119

117120
from ometa.protocol import ParserProtocol
@@ -127,8 +130,8 @@ def stack(*wrappers):
127130
Stack some senders or receivers for ease of wrapping.
128131
129132
``stack(x, y, z)`` will return a factory usable as a sender or receiver
130-
factory which will, when called with a transport or sender, return
131-
``x(y(z(transport-or-sender)))``.
133+
factory which will, when called with a transport or sender as an argument,
134+
return ``x(y(z(argument)))``.
132135
"""
133136
if not wrappers:
134137
raise TypeError('at least one argument is required')

0 commit comments

Comments
 (0)