Skip to content

Commit 189561e

Browse files
committed
major doc reorganization
1 parent da3fb95 commit 189561e

File tree

6 files changed

+208
-200
lines changed

6 files changed

+208
-200
lines changed

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ doc:
1616
rm -rf docs/source/api
1717
cd docs; sphinx-apidoc -e -o source/api ../effect ../setup.py ../examples ../effect/test_*.py
1818
rm docs/source/api/modules.rst
19+
rm docs/source/api/effect.rst
1920
# can't use sed -i on both linux and mac, so...
20-
sed -e 's/Module contents/Core API/' docs/source/api/effect.rst > .effect.rst
21-
mv .effect.rst docs/source/api/effect.rst
21+
# sed -e 's/Module contents/Core API/' docs/source/api/effect.rst > .effect.rst
22+
# mv .effect.rst docs/source/api/effect.rst
23+
# sed -e 's/effect package/API docs/' docs/source/api/effect.rst > .effect.rst
24+
# mv .effect.rst docs/source/api/effect.rst
2225
cd docs; PYTHONPATH=..:$(PYTHONPATH) sphinx-build -W -b html -d build/doctrees source build/html

docs/source/api/effect.rst

Lines changed: 0 additions & 23 deletions
This file was deleted.

docs/source/apidocs.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
API documentation
2+
=================
3+
4+
Core API
5+
--------
6+
7+
.. automodule:: effect
8+
:members:
9+
:undoc-members:
10+
:show-inheritance:
11+
12+
13+
Submodules
14+
----------
15+
16+
.. toctree::
17+
:glob:
18+
19+
api/effect.*

docs/source/index.rst

Lines changed: 6 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -10,185 +10,16 @@ It lives on PyPI at https://pypi.python.org/pypi/effect and GitHub at
1010
https://github.com/python-effect/effect.
1111

1212

13-
API
14-
===
15-
16-
Effect comes with thorough API documentation.
13+
Documentation
14+
=============
1715

1816
.. toctree::
1917
:maxdepth: 3
18+
:numbered:
2019

21-
api/effect
22-
23-
24-
25-
Explanation by Example
26-
======================
27-
28-
Effect starts with a very simple idea: instead of having a function which
29-
performs side-effects (such as IO):
30-
31-
.. code:: python
32-
33-
def get_user_name():
34-
return raw_input("Enter User Name> ") # or 'input' in Python 3
35-
36-
you instead have a function which *returns* a representation of the
37-
side-effect:
38-
39-
.. code:: python
40-
41-
def get_user_name():
42-
return Effect(ReadLine("Enter User Name> "))
43-
44-
We call objects like ``ReadLine`` an *intent* -- that is, the *intent* of this
45-
effect is to read a line of input from the user. Ideally, intents are very
46-
simple objects with public attributes and no behavior, only data.
47-
48-
.. code:: python
49-
50-
class ReadLine(object):
51-
def __init__(self, prompt):
52-
self.prompt = prompt
53-
54-
To perform the ReadLine intent, we must implement a performer function:
55-
56-
.. code:: python
57-
58-
@sync_performer
59-
def perform_read_line(dispatcher, readline):
60-
return raw_input(readline.prompt)
61-
62-
63-
To do something with the result of the effect, we must attach callbacks with
64-
the ``on`` method:
65-
66-
.. code:: python
67-
68-
def greet():
69-
return get_user_name().on(
70-
success=lambda r: Effect(Print("Hello,", r)),
71-
error=lambda exc_info: Effect(Print("There was an error!", exc_info[1])))
72-
73-
74-
(Here we assume another intent, ``Print``, which shows some text to the user.)
75-
76-
A (sometimes) nicer syntax is provided for adding callbacks, with the
77-
:func:`effect.do.do` decorator.
78-
79-
.. code:: python
80-
81-
from effect.do import do
82-
83-
@do
84-
def greet():
85-
try:
86-
name = yield get_user_name()
87-
except Exception as e:
88-
yield Effect(Print("There was an error!", e))
89-
else:
90-
yield Effect(Print("Hello,", name))
91-
92-
Finally, to actually perform these effects, they can be passed to
93-
:func:`effect.perform`, along with a dispatcher which looks up the performer
94-
based on the intent.
95-
96-
.. code:: python
97-
98-
def main():
99-
eff = greet()
100-
dispatcher = TypeDispatcher({ReadLine: perform_read_line})
101-
perform(dispatcher, effect)
102-
103-
This has a number of advantages. First, your unit tests for ``get_user_name``
104-
become simpler. You don't need to mock out or parameterize the ``raw_input``
105-
function - you just call ``get_user_name`` and assert that it returns a
106-
``ReadLine`` object with the correct 'prompt' value.
107-
108-
Second, you can implement ``ReadLine`` in a number of different ways - it's
109-
possible to override the way an intent is performed to do whatever you want. For
110-
example, you could implement an HTTPRequest client either using the popular
111-
`requests`_ package, or using the Twisted-based `treq`_ package -- without
112-
needing to change any of your application code, since it's all in terms of the
113-
Effect API.
114-
115-
.. _`requests`: https://pypi.python.org/pypi/requests
116-
.. _`treq`: https://pypi.python.org/pypi/treq
117-
118-
119-
A quick tour, with definitions
120-
==============================
121-
122-
- Intent: An object which describes a desired action, ideally with simple
123-
inert data in public attributes. For example, ``ReadLine(prompt='> ')`` could
124-
be an intent that describes the desire to read a line from the user after
125-
showing a prompt.
126-
- :obj:`effect.Effect`: An object which binds callbacks to receive the result
127-
of performing an intent.
128-
- Performer: A callable that takes the Dispatcher, an Intent, and a Box. It
129-
executes the Intent and puts the result in the Box. For example, the
130-
performer for ``ReadLine()`` could call ``raw_input(intent.prompt)``.
131-
- Dispatcher: A callable that takes an Intent and finds the Performer that can
132-
execute it (or None). See :obj:`TypeDispatcher` and :obj:`ComposedDispatcher`
133-
for handy pre-built dispatchers.
134-
- Box: An object that has ``succeed`` and ``fail`` methods for providing the
135-
result of an effect (potentially asynchronously). Usually you don't need
136-
to care about this, if you define your performers with
137-
:func:`effect.sync_performer` or :func:`effect.twisted.deferred_performer`.
138-
139-
There's a few main things you need to do to use Effect.
140-
141-
- Define some intents to describe your side-effects (or use a library
142-
containing intents that already exist). For example, an ``HTTPRequest``
143-
intent that has ``method``, ``url``, etc attributes.
144-
- Write your application code to create effects like
145-
``Effect(HTTPRequest(...))`` and attach callbacks to them with
146-
:func:`Effect.on`.
147-
- As close as possible to the top-level of your application, perform your
148-
effect(s) with :func:`effect.perform`.
149-
- You will need to pass a dispatcher to :func:`effect.perform`. You should create one
150-
by creating a :class:`effect.TypeDispatcher` with your own performers (e.g. for
151-
``HTTPRequest``), and composing it with :obj:`effect.base_dispatcher` (which
152-
has performers for built-in effects) using :class:`effect.ComposedDispatcher`.
153-
154-
155-
Callback chains
156-
===============
157-
158-
Effect allows you to build up chains of callbacks that process data in turn.
159-
That is, if you attach a callback ``a`` and then a callback ``b`` to an Effect,
160-
``a`` will be called with the original result, and ``b`` will be called with
161-
the result of ``a``. This is exactly how Twisted's Deferreds work, and similar
162-
to the monadic ``bind`` (``>>=``) function from Haskell.
163-
164-
This is a great way to build abstractions, compared to non-chaining callback
165-
systems like Python's Futures. You can easily build abstractions like the
166-
following:
167-
168-
.. code:: python
169-
170-
def request_url(method, url, str_body):
171-
"""Perform an HTTP request."""
172-
return Effect(HTTPRequest(method, url, str_body))
173-
174-
def request_200_url(method, url, str_body):
175-
"""
176-
Perform an HTTP request, and raise an error if the response is not 200.
177-
"""
178-
def check_status(response):
179-
if response.code != 200:
180-
raise HTTPError(response.code)
181-
return response
182-
return request_url(method, url, str_body).on(success=check_status)
183-
184-
def json_request(method, url, dict_body):
185-
"""
186-
Perform an HTTP request where the body is sent as JSON and the response
187-
is automatically decoded as JSON if the Content-type is
188-
application/json.
189-
"""
190-
str_body = json.dumps(dict_body)
191-
return request_200_url(method, url, str_body).on(success=decode_json)
20+
intro
21+
testing
22+
apidocs
19223

19324

19425
Indices and tables

0 commit comments

Comments
 (0)