Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.

Commit 7d0f250

Browse files
authored
Merge pull request #280 from iotaledger/release/2.2.0b1
PyOTA v2.2.0-beta1
2 parents 3057a1b + aaf2391 commit 7d0f250

File tree

87 files changed

+5764
-2704
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+5764
-2704
lines changed

.travis.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ python:
55
- '3.6'
66
- '3.7'
77
install:
8-
- pip install .
8+
- pip install .[docs-builder]
99
- pip install docutils pygments # Used to check package metadata.
1010
script:
1111
- python setup.py check --strict --metadata --restructuredtext
1212
- nosetests
13+
- cd docs && make html && cd .. # Build documentation.
1314
deploy:
1415
on:
1516
python: '3.7'

CONTRIBUTING.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ We're pretty open about how people contribute to PyOTA, but there are a few thin
2929
- Please do not post support requests here. Use the ``#python`` channel on `Discord`_
3030
- Please do not propose new API methods here. There are multiple IOTA API libraries out there, and they must all have the same functionality.
3131

32-
- That said, if you have an idea for a new API method, please share it on the ``#developers`` channel in `Discord`_ so that IOTA Foundation members can evaluate it!
32+
- That said, if you have an idea for a new API method, please share it on the ``#clients-discussion`` channel in `Discord`_ so that IOTA Foundation members can evaluate it!
3333

3434

3535
Need Some Inspiration?

README.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ Dependencies
2626
============
2727
PyOTA is compatible with Python 3.7, 3.6, 3.5 and 2.7
2828

29-
============
30-
Installation
31-
============
29+
=============
30+
Install PyOTA
31+
=============
3232
To install the latest version::
3333

3434
pip install pyota

docs/adapters.rst

+129-139
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
Adapters and Wrappers
22
=====================
3+
.. py:currentmodule:: iota
34
4-
The ``Iota`` class defines the API methods that are available for
5+
The :py:class:`Iota` class defines the API methods that are available for
56
interacting with the node, but it delegates the actual interaction to
6-
another set of classes: Adapters and Wrappers.
7+
another set of classes: `Adapters <#adapters>`__ and `Wrappers <#wrappers>`__.
8+
9+
The API instance's methods contain the logic and handle PyOTA-specific types,
10+
construct and translate objects, while the API instance's adapter deals with
11+
the networking, communicating with a node.
12+
13+
You can choose and configure the available adapters to be used with the API:
14+
15+
- HttpAdapter,
16+
- MockAdapter.
717

818
AdapterSpec
919
-----------
1020

21+
1122
In a few places in the PyOTA codebase, you may see references to a
1223
meta-type called ``AdapterSpec``.
1324

14-
``AdapterSpec`` is a placeholder that means "URI or adapter instance".
25+
.. automodule:: iota.adapter
26+
:special-members: AdapterSpec
27+
28+
.. py:currentmodule:: iota
29+
30+
For example, when creating an :py:class:`Iota` object, the first argument
31+
of :py:meth:`Iota.__init__` is an ``AdapterSpec``. This means that you can
32+
initialize an :py:class:`Iota` object using either a node URI, or an adapter
33+
instance:
34+
35+
- Node URI::
36+
37+
api = Iota('http://localhost:14265')
1538

16-
For example, the first argument of ``Iota.__init__`` is an
17-
``AdapterSpec``. This means that you can initialize an ``Iota`` object
18-
using either a node URI, or an adapter instance:
39+
- Adapter instance::
1940

20-
- Node URI: ``Iota('http://localhost:14265')``
21-
- Adapter instance: ``Iota(HttpAdapter('http://localhost:14265'))``
41+
api = Iota(HttpAdapter('http://localhost:14265'))
2242

2343
Adapters
2444
--------
@@ -33,113 +53,149 @@ HttpAdapter
3353

3454
.. code:: python
3555
36-
from iota import Iota
37-
from iota.adapter import HttpAdapter
56+
from iota import Iota, HttpAdapter
3857
3958
# Use HTTP:
4059
api = Iota('http://localhost:14265')
4160
api = Iota(HttpAdapter('http://localhost:14265'))
4261
4362
# Use HTTPS:
44-
api = Iota('https://service.iotasupport.com:14265')
45-
api = Iota(HttpAdapter('https://service.iotasupport.com:14265'))
63+
api = Iota('https://nodes.thetangle.org:443')
64+
api = Iota(HttpAdapter('https://nodes.thetangle.org:443'))
4665
4766
# Use HTTPS with basic authentication and 60 seconds timeout:
4867
api = Iota(
4968
HttpAdapter(
50-
'https://service.iotasupport.com:14265',
69+
'https://nodes.thetangle.org:443',
5170
authentication=('myusername', 'mypassword'),
5271
timeout=60))
5372
54-
``HttpAdapter`` uses the HTTP protocol to send requests to the node.
73+
.. autoclass:: HttpAdapter
5574

56-
To configure an ``Iota`` instance to use ``HttpAdapter``, specify an
57-
``http://`` or ``https://`` URI, or provide an ``HttpAdapter`` instance.
75+
To configure an :py:class:`Iota` instance to use :py:class:`HttpAdapter`,
76+
specify an ``http://`` or ``https://`` URI, or provide an
77+
:py:class:`HttpAdapter` instance.
5878

59-
The ``HttpAdapter`` raises a ``BadApiResponse`` exception if the server
79+
The :py:class:`HttpAdapter` raises a ``BadApiResponse`` exception if the server
6080
sends back an error response (due to invalid request parameters, for
6181
example).
6282

6383
Debugging HTTP Requests
6484
^^^^^^^^^^^^^^^^^^^^^^^
65-
66-
.. code:: python
67-
68-
from logging import getLogger
69-
70-
from iota import Iota
71-
72-
api = Iota('http://localhost:14265')
73-
api.adapter.set_logger(getLogger(__name__))
74-
7585
To see all HTTP requests and responses as they happen, attach a
7686
``logging.Logger`` instance to the adapter via its ``set_logger``
7787
method.
7888

79-
Any time the ``HttpAdapter`` sends a request or receives a response, it
89+
Any time the :py:class:`HttpAdapter` sends a request or receives a response, it
8090
will first generate a log message. Note: if the response is an error
81-
response (e.g., due to invalid request parameters), the ``HttpAdapter``
91+
response (e.g., due to invalid request parameters), the :py:class:`HttpAdapter`
8292
will log the request before raising ``BadApiResponse``.
8393

8494
.. note::
8595

86-
``HttpAdapter`` generates log messages with ``DEBUG`` level, so make sure that your logger's ``level`` attribute is set low enough that it doesn't filter these messages!
96+
:py:class:`HttpAdapter` generates log messages with ``DEBUG`` level, so make
97+
sure that your logger's ``level`` attribute is set low enough that it
98+
doesn't filter these messages!
8799

88-
SandboxAdapter
89-
~~~~~~~~~~~~~~
100+
**Logging to console with default format**
90101

91102
.. code:: python
92103
104+
from logging import getLogger, basicConfig, DEBUG
93105
from iota import Iota
94-
from iota.adapter.sandbox import SandboxAdapter
95106
96-
api =\
97-
Iota(
98-
SandboxAdapter(
99-
uri = 'https://sandbox.iotatoken.com/api/v1/',
100-
auth_token = 'demo7982-be4a-4afa-830e-7859929d892c',
101-
),
102-
)
107+
api = Iota("https://nodes.thetangle.org:443")
103108
104-
The ``SandboxAdapter`` is a specialized ``HttpAdapter`` that sends
105-
authenticated requests to sandbox nodes.
109+
# Sets the logging level for the root logger (and for its handlers)
110+
basicConfig(level=DEBUG)
106111
107-
.. note::
112+
# Get a new logger derived from the root logger
113+
logger = getLogger(__name__)
108114
109-
See `Sandbox <https://dev.iota.org/sandbox/>`_ Documentation for more information about sandbox nodes.
115+
# Attach the logger to the adapter
116+
api.adapter.set_logger(logger)
110117
111-
Sandbox nodes process certain commands asynchronously. When
112-
``SandboxAdapter`` determines that a request is processed
113-
asynchronously, it will block, then poll the node periodically until it
114-
receives a response.
118+
# Execute a command that sends request to the node
119+
api.get_node_info()
115120
116-
The result is that ``SandboxAdapter`` abstracts away the sandbox node's
117-
asynchronous functionality so that your API client behaves exactly the
118-
same as if it were connecting to a non-sandbox node.
121+
# Log messages should be printed to console
119122
120-
To create a ``SandboxAdapter``, you must provide the URI of the sandbox
121-
node and the auth token that you received from the node maintainer. Note
122-
that ``SandboxAdapter`` only works with ``http://`` and ``https://``
123-
URIs.
123+
**Logging to a file with custom format**
124124

125-
You may also specify the polling interval (defaults to 15 seconds) and
126-
the number of polls before giving up on an asynchronous job (defaults to
127-
8 times).
125+
.. code:: python
128126
129-
.. note::
127+
from logging import getLogger, DEBUG, FileHandler, Formatter
128+
from iota import Iota
129+
130+
# Create a custom logger
131+
logger = getLogger(__name__)
132+
133+
# Set logging level to DEBUG
134+
logger.setLevel(DEBUG)
135+
136+
# Create handler to write to a log file
137+
f_handler = FileHandler(filename='pyota.log',mode='a')
138+
f_handler.setLevel(DEBUG)
139+
140+
# Create formatter and add it to handler
141+
f_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
142+
f_handler.setFormatter(f_format)
143+
144+
# Add handler to the logger
145+
logger.addHandler(f_handler)
146+
147+
# Create API instance
148+
api = Iota("https://nodes.thetangle.org:443")
149+
150+
# Add logger to the adapter of the API instance
151+
api.adapter.set_logger(logger)
130152
131-
For parity with the other adapters, ``SandboxAdapter`` blocks until it receives a response from the node.
153+
# Sends a request to the node
154+
api.get_node_info()
132155
133-
If you do not want ``SandboxAdapter`` to block the main thread, it is recommended that you execute it in a separate thread or process.
156+
# Open 'pyota.log' file and observe the logs
134157
158+
**Logging to console with custom format**
159+
160+
.. code:: python
161+
162+
from logging import getLogger, DEBUG, StreamHandler, Formatter
163+
from iota import Iota
164+
165+
# Create a custom logger
166+
logger = getLogger(__name__)
167+
168+
# Set logging level to DEBUG
169+
logger.setLevel(DEBUG)
170+
171+
# Create handler to write to sys.stderr
172+
s_handler = StreamHandler()
173+
s_handler.setLevel(DEBUG)
174+
175+
# Create formatter and add it to handler
176+
s_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
177+
s_handler.setFormatter(s_format)
178+
179+
# Add handler to the logger
180+
logger.addHandler(s_handler)
181+
182+
# Create API instance
183+
api = Iota("https://nodes.thetangle.org:443")
184+
185+
# Add logger to the adapter of the API instance
186+
api.adapter.set_logger(logger)
187+
188+
# Sends a request to the node
189+
api.get_node_info()
190+
191+
# Observe log messages in console
135192
136193
MockAdapter
137194
~~~~~~~~~~~
138195

139196
.. code:: python
140197
141-
from iota import Iota
142-
from iota.adapter import MockAdapter
198+
from iota import Iota, MockAdapter
143199
144200
# Inject a mock adapter.
145201
api = Iota('mock://')
@@ -154,35 +210,14 @@ MockAdapter
154210
print(api.get_node_info()) # {'message': 'Hello, IOTA!'}
155211
print(api.get_node_info()) # raises BadApiResponse exception
156212
157-
``MockAdapter`` is used to simulate the behavior of an adapter without
158-
actually sending any requests to the node.
213+
.. autoclass:: MockAdapter
159214

160-
This is particularly useful in unit and functional tests where you want
161-
to verify that your code works correctly in specific scenarios, without
162-
having to engineer your own subtangle.
215+
To use :py:class:`MockAdapter`, you must first seed the responses that you want
216+
it to return by calling its :py:meth:`MockAdapter.seed_response` method.
163217

164-
To configure an ``Iota`` instance to use ``MockAdapter``, specify
165-
``mock://`` as the node URI, or provide a ``MockAdapter`` instance.
166-
167-
To use ``MockAdapter``, you must first seed the responses that you want
168-
it to return by calling its ``seed_response`` method.
169-
170-
``seed_response`` takes two parameters:
171-
172-
- ``command: Text``: The name of the command. Note that this is the
173-
camelCase version of the command name (e.g., ``getNodeInfo``, not
174-
``get_node_info``).
175-
- ``response: dict``: The response that the adapter will return.
176-
177-
You can seed multiple responses for the same command; the
178-
``MockAdapter`` maintains a queue for each command internally, and it
179-
will pop a response off of the corresponding queue each time it
180-
processes a request.
181-
182-
Note that you have to call ``seed_response`` once for each request you
183-
expect it to process. If ``MockAdapter`` does not have a seeded response
184-
for a particular command, it will raise a ``BadApiResponse`` exception
185-
(simulates a 404 response).
218+
**seed_response**
219+
^^^^^^^^^^^^^^^^^
220+
.. automethod:: MockAdapter.seed_response
186221

187222
Wrappers
188223
--------
@@ -192,53 +227,8 @@ otherwise modify the behavior of adapters.
192227

193228
RoutingWrapper
194229
~~~~~~~~~~~~~~
230+
.. autoclass:: iota.adapter.wrappers.RoutingWrapper
195231

196-
.. code:: python
197-
198-
from iota import Iota
199-
from iota.adapter.wrappers import RoutingWrapper
200-
201-
api =\
202-
Iota(
203-
# Send PoW requests to local node.
204-
# All other requests go to light wallet node.
205-
RoutingWrapper('https://service.iotasupport.com:14265')
206-
.add_route('attachToTangle', 'http://localhost:14265')
207-
.add_route('interruptAttachingToTangle', 'http://localhost:14265')
208-
)
209-
210-
``RoutingWrapper`` allows you to route API requests to different nodes
211-
depending on the command name.
212-
213-
For example, you could use this wrapper to direct all PoW requests to a
214-
local node, while sending the other requests to a light wallet node.
215-
216-
.. note::
217-
218-
A common use case for ``RoutingWrapper`` is to perform proof-of-work on
219-
a specific (local) node, but let all other requests go to another node.
220-
Take care when you use ``RoutingWrapper`` adapter and ``local_pow``
221-
parameter together in an API instance, because the behavior might not
222-
be obvious.
223-
224-
``local_pow`` tells the API to perform proof-of-work (``attach_to_tangle``)
225-
without relying on an actual node. It does this by calling an extension
226-
package `PyOTA-PoW <https://pypi.org/project/PyOTA-PoW/>`_ that does the
227-
job. In PyOTA, this means the request doesn't reach the adapter, it
228-
is redirected before.
229-
As a consequence, ``local_pow`` has precedence over the route that is
230-
defined in ``RoutingWrapper``.
231-
232-
``RoutingWrapper`` must be initialized with a default URI/adapter. This
233-
is the adapter that will be used for any command that doesn't have a
234-
route associated with it.
235-
236-
Once you've initialized the ``RoutingWrapper``, invoke its ``add_route``
237-
method to specify a different adapter to use for a particular command.
238-
239-
``add_route`` requires two arguments:
240-
241-
- ``command: Text``: The name of the command. Note that this is the
242-
camelCase version of the command name (e.g., ``getNodeInfo``, not
243-
``get_node_info``).
244-
- ``adapter: AdapterSpec``: The adapter or URI to send this request to.
232+
**add_route**
233+
^^^^^^^^^^^^^
234+
.. automethod:: iota.adapter.wrappers.RoutingWrapper.add_route

0 commit comments

Comments
 (0)