Skip to content

Commit

Permalink
v1.1.0 - Major update - return instances instead of dicts
Browse files Browse the repository at this point in the history
**Data classes / objects**

As many of us know, it gets tiring having to constantly type `x['balance']` when
something returns a dictionary, especially when there's no IDE completion for it.

To solve this problem, new classes have been written to represent SteemEngine objects, such
as balances, transactions, and tokens. This allows you to access keys simply through
`x.balance` instead.

For backwards compatibility, the `ObjBase` parent class allows instances of these data classes
to be treated like dictionaries/lists, so `x['balance']` will still work, and it will return
the same format as before, while `x.balance` for example, would return the balance as a
proper `Decimal` object.

You can also convert the classes into dicts/lists with `dict(x)` and `list(x)`, but be
aware that this uses the `raw_data` that was originally passed, so these converted objects
will not have their keys casted to the appropriate Python types, unless they were casted
before being passed into the class.

 - New module `privex.steemengine.objects` for classes designed to hold data
     - `ObjBase` is used as the base class for the data classes. For backwards compatibility,
       it allows instances of the classes to be accessed like a dictionary/list,
       offers `from_list` for easy conversions of `list<dict>` into their respective
       data classes, and also implements `__iter__` so instances can be converted
       using `dict(x)` and `list(x)`.
     - `Token` represents the data of a SteemEngine token such as name/symbol/supply
     - `SEBalance` represents a balance with account/symbol/balance
     - `SETransaction` represents a history tx with txid/timestamp/quantity etc.
     - `TokenMetadata` represents the `metadata` field of token data

**Upgraded SteemEngineToken to use the data classes**

The **SteemEngineToken** class has been updated to use the new data classes. Thanks to
`ObjBase`, this should hopefully not break any existing applications, but there's always a risk.

The methods that have been updated:

 - `list_tokens` now returns `List[Token]` instead of `List[dict]`
 - `get_token` now returns `Token` instead of `dict`
 - `get_token_balances` now returns `List[SEBalance]` instead of `List[dict]`
 - `list_transactions` now returns `List[SETransaction]` instead of `List[dict]`

**Documentation for the new data classes**

The documentation has been updated with details on how the data classes work.

The documentation for **SteemEngineToken** methods have *mostly* been updated to
show usage of the new data classes instead of `dict`s.
  • Loading branch information
Someguy123 committed Jul 21, 2019
1 parent f03efc2 commit e0cd844
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/source/code/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
privex.steemengine.SteemEngineToken.SteemEngineToken
privex.steemengine.SteemEngineHistory.SteemEngineHistory
privex.steemengine.exceptions
privex.steemengine.objects
tests
27 changes: 27 additions & 0 deletions docs/source/code/privex.steemengine.objects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Data Objects (Token/SEBalance etc.)
====================================

.. automodule:: privex.steemengine.objects







.. rubric:: Classes

.. autosummary::
:toctree: stubs

ObjBase
SEBalance
SETransaction
Token
TokenMetadata






23 changes: 23 additions & 0 deletions docs/source/code/stubs/privex.steemengine.objects.ObjBase.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
objects.ObjBase
==================================

.. currentmodule:: privex.steemengine.objects

.. autoclass:: ObjBase


.. automethod:: __init__


.. rubric:: Methods

.. autosummary::

~ObjBase.__init__
~ObjBase.from_list






23 changes: 23 additions & 0 deletions docs/source/code/stubs/privex.steemengine.objects.SEBalance.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
objects.SEBalance
====================================

.. currentmodule:: privex.steemengine.objects

.. autoclass:: SEBalance


.. automethod:: __init__


.. rubric:: Methods

.. autosummary::

~SEBalance.__init__
~SEBalance.from_list






Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
objects.SETransaction
========================================

.. currentmodule:: privex.steemengine.objects

.. autoclass:: SETransaction


.. automethod:: __init__


.. rubric:: Methods

.. autosummary::

~SETransaction.__init__
~SETransaction.from_list






23 changes: 23 additions & 0 deletions docs/source/code/stubs/privex.steemengine.objects.Token.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
objects.Token
================================

.. currentmodule:: privex.steemengine.objects

.. autoclass:: Token


.. automethod:: __init__


.. rubric:: Methods

.. autosummary::

~Token.__init__
~Token.from_list






Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
objects.TokenMetadata
========================================

.. currentmodule:: privex.steemengine.objects

.. autoclass:: TokenMetadata


.. automethod:: __init__


.. rubric:: Methods

.. autosummary::

~TokenMetadata.__init__
~TokenMetadata.from_list






47 changes: 27 additions & 20 deletions privex/steemengine/SteemEngineToken.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from privex.jsonrpc import SteemEngineRPC
from privex.steemengine import exceptions
from privex.steemengine.SteemEngineHistory import SteemEngineHistory
from privex.steemengine.objects import SEBalance, SETransaction, Token
from privex.helpers import empty

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -96,28 +97,28 @@ def custom_beem(node: Union[str, list] = "", *args, **kwargs):
SteemEngineToken._steem = Steem(node, *args, **kwargs)
return SteemEngineToken._steem

def get_balances(self, user) -> List[dict]:
def get_balances(self, user) -> List[SEBalance]:
"""
Get all token balances for a user.
**Example:**
>>> balances = SteemEngineToken().get_balances('someguy123')
>>> for bal in balances:
... print(f"{bal['symbol']} balance is: {bal['balance']}")
... print(f"{bal.symbol} balance is: {bal.balance}")
ENG balance is: 12.345
SGTK balance is: 51235
STEEMP balance is: 102.437
:param user: Username to find all token balances for
:return list<dict> balances: All balances of a user [{account:str, symbol:str, balance:str}...]
:return list<SEBalance> balances: All balances of a user [{account:str, symbol:str, balance:str}...]
"""
log.debug('Finding all token balances for user %s', user)
return self.rpc.find(
return list(SEBalance.from_list(self.rpc.find(
contract='tokens',
table='balances',
query=dict(account=user)
)
)))

def get_token_balance(self, user, symbol) -> Decimal:
"""
Expand Down Expand Up @@ -159,22 +160,22 @@ def account_exists(self, user) -> bool:
log.debug('Checking if user %s exists', user)
return len(self.steem.rpc.get_account(user)) > 0

def list_tokens(self, limit=1000, offset=0) -> List[dict]:
def list_tokens(self, limit=1000, offset=0) -> List[Token]:
"""
Returns a list of all tokens.
Returns a list of all tokens as :class:`.Token` objects.
**Example:**
>>> for t in SteemEngineToken().list_tokens():
... print(f"Token {t['symbol']} has a max supply of {t['maxSupply']} and issued by {t['issuer']}")
... print(f"Token {t.symbol} has a max supply of {t.max_supply} and issued by {t.issuer}")
Token ENG has a max supply of 9007199254740991 and issued by null
Token STEEMP has a max supply of 1000000000000 and issued by steem-peg
Token BTCP has a max supply of 1000000000000 and issued by btcpeg
Token LTCP has a max supply of 1000000000000 and issued by ltcp
:param limit: Amount of token objects to retrieve
:param offset: Amount of token objects to skip (for pagination)
:return list<dict> tokens: Each list item formatted like this:
:return List<Token> tokens: Each :class:`.Token` list item formatted like this:
.. code-block:: js
Expand All @@ -190,12 +191,12 @@ def list_tokens(self, limit=1000, offset=0) -> List[dict]:
}
"""
return self.rpc.find(
return list(Token.from_list(self.rpc.find(
contract='tokens',
table='tokens',
query={},
limit=limit, offset=offset
)
)))

def find_steem_tx(self, tx_data: dict, last_blocks=15) -> dict:
"""
Expand Down Expand Up @@ -228,14 +229,14 @@ def find_steem_tx(self, tx_data: dict, last_blocks=15) -> dict:
return tx
return None

def list_transactions(self, user, symbol=None, limit=100, offset=0) -> List[dict]:
def list_transactions(self, user, symbol=None, limit=100, offset=0) -> List[SETransaction]:
"""
Get the Steem Engine transaction history for a given account
**Example:**
>>> for tx in SteemEngineToken().list_transactions('someguy123'):
... print(tx['timestamp'], tx['from'], 'sent', tx['quantity'], tx['symbol'], 'to', tx['to'])
... print(tx.timestamp, tx.sender, 'sent', tx.quantity, tx.symbol, 'to', tx.to)
2019-07-04T06:18:09.000Z market sent 100 SGTK to someguy123
2019-07-04T01:01:15.000Z minnowsupport sent 0.924 PAL to someguy123
2019-07-03T17:10:36.000Z someguy123 sent 1 BTSP to btsp
Expand All @@ -244,27 +245,32 @@ def list_transactions(self, user, symbol=None, limit=100, offset=0) -> List[dict
:param str symbol: Symbol to filter by, e.g. ENG (optional)
:param int limit: Return this many transactions (optional)
:param int offset: Skip this many transactions (for pagination) (optional)
:return List<dict> txs: A list of ``dict(block, txid, timestamp, symbol, from, from_type, to, to_type, memo, quantity)``
:return List<SETransaction> txs: A list of :class:`.SETransaction` containing
block, txid, timestamp, symbol, sender, from_type, to, to_type, memo, quantity
"""
symbol = None if empty(symbol) else symbol.upper()
log.debug('Getting TX history for user %s, symbol %s, limit %s, offset %s', user, symbol, limit, offset)
return self.history_rpc.get_history(account=user, symbol=symbol, limit=limit, offset=offset)
return list(SETransaction.from_list(self.history_rpc.get_history(account=user, symbol=symbol, limit=limit, offset=offset)))

def get_token(self, symbol) -> dict:
def get_token(self, symbol) -> Token:
"""
Get the token object for an individual token.
**Example:**
>>> token = SteemEngineToken().get_token('SGTK'):
>>> print(token['issuer'], token['name'])
>>> print(token.issuer, token.name)
someguy123 SomeToken
:param str symbol: Symbol of the token to lookup, such as 'ENG'
:return dict token_data: A dictionary containing data about the token (see below)
:return Token token_data: An object containing data about the token (see below)
Formatted like below:
A :class:`.Token` object can be accessed either via attributes ``token.issuer`` or as a dict.
They contain the fields below:
.. code-block:: js
Expand All @@ -282,11 +288,12 @@ def get_token(self, symbol) -> dict:
:return None: If token not found, ``None`` is returned.
"""
log.debug('Getting token object for symbol %s', symbol)
return self.rpc.findone(
tk = self.rpc.findone(
contract='tokens',
table='tokens',
query=dict(symbol=symbol.upper())
)
return None if empty(tk) else Token(**tk)

def send_token(self, symbol, from_acc, to_acc, amount: Decimal, memo="", find_tx=True) -> dict:
"""
Expand Down
1 change: 1 addition & 0 deletions privex/steemengine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys
from privex.steemengine.SteemEngineToken import SteemEngineToken
from privex.steemengine.SteemEngineHistory import SteemEngineHistory
from privex.steemengine.objects import Token, TokenMetadata, SEBalance, SETransaction, ObjBase

name = 'steemengine'

Expand Down
Loading

0 comments on commit e0cd844

Please sign in to comment.