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

PyOTA v2.2.0-beta1 #280

Merged
merged 36 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
370cc6d
Merge pull request #248 from iotaledger/master
lzpap Oct 21, 2019
07f344c
Implement broadcast_bundle() Api Command
lzpap Oct 22, 2019
35a274c
Code polish after PR review
lzpap Oct 23, 2019
4ae8a61
Merge pull request #249 from lzpap/broadcast_bundle
lzpap Oct 23, 2019
aa878fb
Implement is_promotable Extended API Command
lzpap Oct 29, 2019
2cacc37
Refactor traverse_bundle into Extended Api
lzpap Oct 30, 2019
d9a61a6
is_promotable: Test improvements and optimization
lzpap Nov 5, 2019
262ff70
Merge pull request #256 from lzpap/refactor_traverse_bundle
lzpap Nov 5, 2019
a68254d
Merge pull request #255 from lzpap/is_promotable
lzpap Nov 5, 2019
dba8acf
Improve API documentation
lzpap Nov 12, 2019
e033dc8
Merge pull request #261 from lzpap/api_docs_rethink
lzpap Nov 14, 2019
9fa1727
Add docs build step to Travis CI
lzpap Nov 14, 2019
86b53fa
Merge pull request #264 from lzpap/travis_docs
lzpap Nov 14, 2019
22b4f46
add_signature_or_message method for ProposedBundle
lzpap Nov 14, 2019
f3d20c0
Merge pull request #258 from lzpap/add_signature_or_message
lzpap Nov 18, 2019
d6453e5
Support adding/removing address checksums
lzpap Nov 15, 2019
7c647c4
Fine-tune is_promotable response dictionary
lzpap Nov 18, 2019
2e73a66
Merge pull request #265 from lzpap/address_methods
lzpap Nov 19, 2019
6090958
Merge pull request #267 from lzpap/is_promotable_return_type
lzpap Nov 19, 2019
16226e8
docs: add basic concepts page
lzpap Nov 20, 2019
fdab9df
Merge pull request #269 from lzpap/docs_basic_concepts
lzpap Nov 22, 2019
1671976
Expose `Seed` class in the top level iota package
lzpap Nov 28, 2019
fb73a86
docs: re-write PyOTA types page
lzpap Nov 28, 2019
3395a41
Add `wereAddressesSpentFrom` check to `iter_used_addresses` and `get_…
lzpap Nov 28, 2019
28c249b
Merge pull request #270 from lzpap/docs_pyota_types
lzpap Dec 2, 2019
cd370a3
Merge pull request #271 from lzpap/pdecol_snapshot_pr_cleaned
lzpap Dec 2, 2019
7349985
docs: improve `Generating Addresses` page
lzpap Nov 29, 2019
3bf021d
Merge pull request #272 from lzpap/docs_addresses
lzpap Dec 3, 2019
c8508cf
docs: improve `Adapters and Wrappers` page
lzpap Dec 3, 2019
29d01e6
Merge pull request #274 from lzpap/docs_adapters
lzpap Dec 5, 2019
d33fa28
Refactor commands for API classes
lzpap Dec 9, 2019
be39875
docs: add `PyOTA Commands` page
lzpap Dec 9, 2019
585ab08
Merge pull request #275 from lzpap/docs_advanced_pyota_commands
lzpap Dec 11, 2019
51170b8
Remove Sandbox Adapter
lzpap Dec 10, 2019
832e3f3
Merge pull request #279 from lzpap/deprecate_sandbox_adapter
lzpap Dec 11, 2019
aaf2391
Bump version number to 2.2.0b1
lzpap Dec 11, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ python:
- '3.6'
- '3.7'
install:
- pip install .
- pip install .[docs-builder]
- pip install docutils pygments # Used to check package metadata.
script:
- python setup.py check --strict --metadata --restructuredtext
- nosetests
- cd docs && make html && cd .. # Build documentation.
deploy:
on:
python: '3.7'
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ We're pretty open about how people contribute to PyOTA, but there are a few thin
- Please do not post support requests here. Use the ``#python`` channel on `Discord`_
- Please do not propose new API methods here. There are multiple IOTA API libraries out there, and they must all have the same functionality.

- 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!
- 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!


Need Some Inspiration?
Expand Down
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ Dependencies
============
PyOTA is compatible with Python 3.7, 3.6, 3.5 and 2.7

============
Installation
============
=============
Install PyOTA
=============
To install the latest version::

pip install pyota
Expand Down
268 changes: 129 additions & 139 deletions docs/adapters.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
Adapters and Wrappers
=====================
.. py:currentmodule:: iota

The ``Iota`` class defines the API methods that are available for
The :py:class:`Iota` class defines the API methods that are available for
interacting with the node, but it delegates the actual interaction to
another set of classes: Adapters and Wrappers.
another set of classes: `Adapters <#adapters>`__ and `Wrappers <#wrappers>`__.

The API instance's methods contain the logic and handle PyOTA-specific types,
construct and translate objects, while the API instance's adapter deals with
the networking, communicating with a node.

You can choose and configure the available adapters to be used with the API:

- HttpAdapter,
- MockAdapter.

AdapterSpec
-----------


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

``AdapterSpec`` is a placeholder that means "URI or adapter instance".
.. automodule:: iota.adapter
:special-members: AdapterSpec

.. py:currentmodule:: iota

For example, when creating an :py:class:`Iota` object, the first argument
of :py:meth:`Iota.__init__` is an ``AdapterSpec``. This means that you can
initialize an :py:class:`Iota` object using either a node URI, or an adapter
instance:

- Node URI::

api = Iota('http://localhost:14265')

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

- Node URI: ``Iota('http://localhost:14265')``
- Adapter instance: ``Iota(HttpAdapter('http://localhost:14265'))``
api = Iota(HttpAdapter('http://localhost:14265'))

Adapters
--------
Expand All @@ -33,113 +53,149 @@ HttpAdapter

.. code:: python

from iota import Iota
from iota.adapter import HttpAdapter
from iota import Iota, HttpAdapter

# Use HTTP:
api = Iota('http://localhost:14265')
api = Iota(HttpAdapter('http://localhost:14265'))

# Use HTTPS:
api = Iota('https://service.iotasupport.com:14265')
api = Iota(HttpAdapter('https://service.iotasupport.com:14265'))
api = Iota('https://nodes.thetangle.org:443')
api = Iota(HttpAdapter('https://nodes.thetangle.org:443'))

# Use HTTPS with basic authentication and 60 seconds timeout:
api = Iota(
HttpAdapter(
'https://service.iotasupport.com:14265',
'https://nodes.thetangle.org:443',
authentication=('myusername', 'mypassword'),
timeout=60))

``HttpAdapter`` uses the HTTP protocol to send requests to the node.
.. autoclass:: HttpAdapter

To configure an ``Iota`` instance to use ``HttpAdapter``, specify an
``http://`` or ``https://`` URI, or provide an ``HttpAdapter`` instance.
To configure an :py:class:`Iota` instance to use :py:class:`HttpAdapter`,
specify an ``http://`` or ``https://`` URI, or provide an
:py:class:`HttpAdapter` instance.

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

Debugging HTTP Requests
^^^^^^^^^^^^^^^^^^^^^^^

.. code:: python

from logging import getLogger

from iota import Iota

api = Iota('http://localhost:14265')
api.adapter.set_logger(getLogger(__name__))

To see all HTTP requests and responses as they happen, attach a
``logging.Logger`` instance to the adapter via its ``set_logger``
method.

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

.. note::

``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!
:py:class:`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!

SandboxAdapter
~~~~~~~~~~~~~~
**Logging to console with default format**

.. code:: python

from logging import getLogger, basicConfig, DEBUG
from iota import Iota
from iota.adapter.sandbox import SandboxAdapter

api =\
Iota(
SandboxAdapter(
uri = 'https://sandbox.iotatoken.com/api/v1/',
auth_token = 'demo7982-be4a-4afa-830e-7859929d892c',
),
)
api = Iota("https://nodes.thetangle.org:443")

The ``SandboxAdapter`` is a specialized ``HttpAdapter`` that sends
authenticated requests to sandbox nodes.
# Sets the logging level for the root logger (and for its handlers)
basicConfig(level=DEBUG)

.. note::
# Get a new logger derived from the root logger
logger = getLogger(__name__)

See `Sandbox <https://dev.iota.org/sandbox/>`_ Documentation for more information about sandbox nodes.
# Attach the logger to the adapter
api.adapter.set_logger(logger)

Sandbox nodes process certain commands asynchronously. When
``SandboxAdapter`` determines that a request is processed
asynchronously, it will block, then poll the node periodically until it
receives a response.
# Execute a command that sends request to the node
api.get_node_info()

The result is that ``SandboxAdapter`` abstracts away the sandbox node's
asynchronous functionality so that your API client behaves exactly the
same as if it were connecting to a non-sandbox node.
# Log messages should be printed to console

To create a ``SandboxAdapter``, you must provide the URI of the sandbox
node and the auth token that you received from the node maintainer. Note
that ``SandboxAdapter`` only works with ``http://`` and ``https://``
URIs.
**Logging to a file with custom format**

You may also specify the polling interval (defaults to 15 seconds) and
the number of polls before giving up on an asynchronous job (defaults to
8 times).
.. code:: python

.. note::
from logging import getLogger, DEBUG, FileHandler, Formatter
from iota import Iota

# Create a custom logger
logger = getLogger(__name__)

# Set logging level to DEBUG
logger.setLevel(DEBUG)

# Create handler to write to a log file
f_handler = FileHandler(filename='pyota.log',mode='a')
f_handler.setLevel(DEBUG)

# Create formatter and add it to handler
f_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
f_handler.setFormatter(f_format)

# Add handler to the logger
logger.addHandler(f_handler)

# Create API instance
api = Iota("https://nodes.thetangle.org:443")

# Add logger to the adapter of the API instance
api.adapter.set_logger(logger)

For parity with the other adapters, ``SandboxAdapter`` blocks until it receives a response from the node.
# Sends a request to the node
api.get_node_info()

If you do not want ``SandboxAdapter`` to block the main thread, it is recommended that you execute it in a separate thread or process.
# Open 'pyota.log' file and observe the logs

**Logging to console with custom format**

.. code:: python

from logging import getLogger, DEBUG, StreamHandler, Formatter
from iota import Iota

# Create a custom logger
logger = getLogger(__name__)

# Set logging level to DEBUG
logger.setLevel(DEBUG)

# Create handler to write to sys.stderr
s_handler = StreamHandler()
s_handler.setLevel(DEBUG)

# Create formatter and add it to handler
s_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
s_handler.setFormatter(s_format)

# Add handler to the logger
logger.addHandler(s_handler)

# Create API instance
api = Iota("https://nodes.thetangle.org:443")

# Add logger to the adapter of the API instance
api.adapter.set_logger(logger)

# Sends a request to the node
api.get_node_info()

# Observe log messages in console

MockAdapter
~~~~~~~~~~~

.. code:: python

from iota import Iota
from iota.adapter import MockAdapter
from iota import Iota, MockAdapter

# Inject a mock adapter.
api = Iota('mock://')
Expand All @@ -154,35 +210,14 @@ MockAdapter
print(api.get_node_info()) # {'message': 'Hello, IOTA!'}
print(api.get_node_info()) # raises BadApiResponse exception

``MockAdapter`` is used to simulate the behavior of an adapter without
actually sending any requests to the node.
.. autoclass:: MockAdapter

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

To configure an ``Iota`` instance to use ``MockAdapter``, specify
``mock://`` as the node URI, or provide a ``MockAdapter`` instance.

To use ``MockAdapter``, you must first seed the responses that you want
it to return by calling its ``seed_response`` method.

``seed_response`` takes two parameters:

- ``command: Text``: The name of the command. Note that this is the
camelCase version of the command name (e.g., ``getNodeInfo``, not
``get_node_info``).
- ``response: dict``: The response that the adapter will return.

You can seed multiple responses for the same command; the
``MockAdapter`` maintains a queue for each command internally, and it
will pop a response off of the corresponding queue each time it
processes a request.

Note that you have to call ``seed_response`` once for each request you
expect it to process. If ``MockAdapter`` does not have a seeded response
for a particular command, it will raise a ``BadApiResponse`` exception
(simulates a 404 response).
**seed_response**
^^^^^^^^^^^^^^^^^
.. automethod:: MockAdapter.seed_response

Wrappers
--------
Expand All @@ -192,53 +227,8 @@ otherwise modify the behavior of adapters.

RoutingWrapper
~~~~~~~~~~~~~~
.. autoclass:: iota.adapter.wrappers.RoutingWrapper

.. code:: python

from iota import Iota
from iota.adapter.wrappers import RoutingWrapper

api =\
Iota(
# Send PoW requests to local node.
# All other requests go to light wallet node.
RoutingWrapper('https://service.iotasupport.com:14265')
.add_route('attachToTangle', 'http://localhost:14265')
.add_route('interruptAttachingToTangle', 'http://localhost:14265')
)

``RoutingWrapper`` allows you to route API requests to different nodes
depending on the command name.

For example, you could use this wrapper to direct all PoW requests to a
local node, while sending the other requests to a light wallet node.

.. note::

A common use case for ``RoutingWrapper`` is to perform proof-of-work on
a specific (local) node, but let all other requests go to another node.
Take care when you use ``RoutingWrapper`` adapter and ``local_pow``
parameter together in an API instance, because the behavior might not
be obvious.

``local_pow`` tells the API to perform proof-of-work (``attach_to_tangle``)
without relying on an actual node. It does this by calling an extension
package `PyOTA-PoW <https://pypi.org/project/PyOTA-PoW/>`_ that does the
job. In PyOTA, this means the request doesn't reach the adapter, it
is redirected before.
As a consequence, ``local_pow`` has precedence over the route that is
defined in ``RoutingWrapper``.

``RoutingWrapper`` must be initialized with a default URI/adapter. This
is the adapter that will be used for any command that doesn't have a
route associated with it.

Once you've initialized the ``RoutingWrapper``, invoke its ``add_route``
method to specify a different adapter to use for a particular command.

``add_route`` requires two arguments:

- ``command: Text``: The name of the command. Note that this is the
camelCase version of the command name (e.g., ``getNodeInfo``, not
``get_node_info``).
- ``adapter: AdapterSpec``: The adapter or URI to send this request to.
**add_route**
^^^^^^^^^^^^^
.. automethod:: iota.adapter.wrappers.RoutingWrapper.add_route
Loading