"""Cryptoassets application manager."""
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from .models import Base
from .utils.enum import AutoNumber
from .utils.conflictresolver import ConflictResolver
[docs]class Subsystem(AutoNumber):
"""Enumerator for available cryptoassets library subsystems.
Depending on your application entry point and user case, you might not want to initialize all features of cryptoassets framework within your Python application. For example, multiple web server processes cannot initialize status server each, but this functinonality is purposed for the daemon applications.
"""
#: Initialize database connections
database = ()
#: Open HTTP status server running
status_server = ()
#: Try to connect to backend APIs
backend = ()
#: Start processes and threads for broadcasting outgoing transactions
broadcast = ()
#: Start processes and threads for walletnotify hooks
incoming_transactions = ()
#: Post notifications
event_handler_registry = ()
ALL_SUBSYSTEMS = Subsystem.__members__.values()
[docs]class CryptoAssetsApp:
"""This class ties all strings together to make a runnable cryptoassets app."""
def __init__(self, subsystems=[Subsystem.database, Subsystem.backend]):
"""Initialize a cryptoassets framework.
:param subsystems: Give the list of subsystems you want to initialize. Because the same configuration file can be used by e.g. both web server and command line application, and config file parts like status server are only relevant in the context of the command line application, this can tell the cryptoassets framework how to set up itself. By default it initializes all the subsystems.
"""
self.subsystems = subsystems
#: SQLAlchemy database used engine
self.engine = None
#: cryptoassets.core.coin.registry.CoinRegistry instance
self.coins = {}
#: Dict of notify handlers
self.event_handler_registry = {}
self.session = None
#: Configured status server
#: See notes in cryptoassets.core.service.main.Service
self.status_server = None
#: The number of attempts we try to replay conflicted transactions. Set by configuration.
self.transaction_retries = None
#: cryptoassets.core.utils.conflictresolver.ConflictResolver instance we use to resolve database conflicts
self.conflict_resolver = None
[docs] def is_enabled(self, subsystem):
"""Are we running with a specific subsystem enabled."""
return subsystem in self.subsystems
[docs] def setup_session(self, transaction_retries=3):
"""Configure SQLAlchemy models and transaction conflict resolutoin.
Also, bind created cryptocurrency models to their configured backends.
"""
if not self.is_enabled(Subsystem.database):
raise RuntimeError("Database subsystem was not enabled")
self.Session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=self.engine))
self.conflict_resolver = ConflictResolver(self.open_session, self.transaction_retries)
for name, coin in self.coins.all():
coin.wallet_model.backend = coin.backend
coin.address_model.backend = coin.backend
coin.transaction_model.backend = coin.backend
coin.account_model.backend = coin.backend
[docs] def open_session(self):
"""Get new read-write session for the database."""
return self.Session()
[docs] def open_readonly_session(self):
"""Get new read-only access to database.
This session can never write to db, so db can ignore transactions and optimize for speed.
TODO
"""
raise NotImplementedError()
[docs] def create_tables(self):
"""Create database tables.
Usually call only once when settings up the production database, or every time unit test case runs.
"""
if not self.is_enabled(Subsystem.database):
raise RuntimeError("Database subsystem was not enabled")
Base.metadata.create_all(self.engine)
[docs] def clear_tables(self):
"""Delete all data in the database, but leaving tables intact.
Useful to get clean state in unit testing.
.. warning ::
No questions asked. Don't dare to call outside testing or your data is really gone.
"""
for name, coin in self.coins.all():
with self.conflict_resolver.transaction() as session:
session.query(coin.wallet_model).delete()
session.query(coin.transaction_model).delete()
session.query(coin.network_transaction_model).delete()
session.query(coin.account_model).delete()
session.query(coin.address_model).delete()