diff --git a/CHANGELOG b/CHANGELOG index 2ad7ceb..3e3cbd4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +- 0.1.2 + * Add CLI inspection tools +- 0.1.1 + * - 0.1.0 * Replace state transitions with shep - 0.0.3 diff --git a/chainqueue/cli/output.py b/chainqueue/cli/output.py new file mode 100644 index 0000000..3ef912c --- /dev/null +++ b/chainqueue/cli/output.py @@ -0,0 +1,153 @@ +# standard imports +import logging +import enum + +# external imports +from hexathon import add_0x + +# local imports +from chainqueue.enum import ( + StatusBits, + all_errors, + is_alive, + is_error_status, + status_str, + ) + +logg = logging.getLogger(__name__) + + +class OutputCol(enum.Enum): + chainspec = 0 + hash = 1 + statustext = 2 + statuscode = 3 + signedtx = 4 + + +class Outputter: + """Output helper for chainqueue cli listings tools. + + :param chain_spec: Chain spec to use as getter context + :type chain_spec: chainlib.chain.ChainSpec + :param writer: Writer to write output to. Will automatically flush. + :type writer: Writer + :param getter: Transaction getter + :type getter: See chainqueue.sql.backend.get_otx + :param session_method: Backend session generator method + :type session_method: varies + :param decode_status: Print status bit details + :type decode_status: bool + """ + + all_cols = [ + OutputCol.chainspec, + OutputCol.hash, + OutputCol.signedtx, + OutputCol.statustext, + OutputCol.statuscode, + ] + default_cols = [ + OutputCol.chainspec, + OutputCol.hash, + OutputCol.statustext, + OutputCol.statuscode, + ] + + def __init__(self, chain_spec, writer, getter, session_method=None, decode_status=True, cols=None): + self.decode_status = decode_status + self.writer = writer + self.getter = getter + self.chain_spec = chain_spec + self.chain_spec_str = str(chain_spec) + self.session = None + if session_method != None: + self.session = session_method() + self.results = { + 'pending_error': 0, + 'final_error': 0, + 'pending': 0, + 'final': 0, + } + + debug_col_name = [] + if cols == None: + self.cols = Outputter.default_cols + else: + self.cols = [] + for col in cols: + v = getattr(OutputCol, col) + self.cols.append(v) + + for col in self.cols: + debug_col_name.append(col.name) + logg.debug('outputter initialized with cols: {}'.format(','.join(debug_col_name))) + + + def __del__(self): + if self.session != None: + self.session.close() + + + def add(self, tx_hash): + """Retrieve a transaction by hash and add it for summary output generation. + + :param tx_hash: Transaction hash + :type tx_hash: str + """ + tx = self.getter(self.chain_spec, tx_hash, session=self.session) + self.__add(tx) + + def __add(self, tx): + category = None + if is_alive(tx['status_code']): + category = 'pending' + else: + category = 'final' + self.results[category] += 1 + if is_error_status(tx['status_code']): + logg.debug('registered {} as {} with error'.format(tx['tx_hash'], category)) + self.results[category + '_error'] += 1 + else: + logg.debug('registered {} as {}'.format(tx['tx_hash'], category)) + + + def decode_summary(self): + """Writes summary to the registered writer. + """ + self.writer.write('pending\t{}\t{}\n'.format(self.results['pending'], self.results['pending_error'])) + self.writer.write('final\t{}\t{}\n'.format(self.results['final'], self.results['final_error'])) + self.writer.write('total\t{}\t{}\n'.format(self.results['final'] + self.results['pending'], self.results['final_error'] + self.results['pending_error'])) + + + def decode_single(self, tx_hash): + """Retrieves the transaction with the given hash and writes the details to the underlying writer. + + Registers the transaction with the summary generator. + + :param tx_hash: Transaction hash + :type tx_hash: str + """ + tx = self.getter(self.chain_spec, tx_hash, session=self.session) + self.__add(tx) + status = tx['status'] + if self.decode_status: + status = status_str(tx['status_code'], bits_only=True) + + vals = [ + self.chain_spec_str, + add_0x(tx_hash), + status, + str(tx['status_code']), + add_0x(tx['signed_tx']), + ] + + i = 0 + l = len(self.cols) + for col in self.cols: + self.writer.write(vals[col.value]) + i += 1 + if i == l: + self.writer.write('\n') + else: + self.writer.write('\t') diff --git a/chainqueue/runnable/list.py b/chainqueue/runnable/list.py index 06d517e..68fe3b9 100644 --- a/chainqueue/runnable/list.py +++ b/chainqueue/runnable/list.py @@ -12,7 +12,7 @@ from chainlib.chain import ChainSpec from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer # local imports -from chainqueue.cli import Outputter +from chainqueue.cli.output import Outputter logging.basicConfig(level=logging.WARNING) diff --git a/setup.cfg b/setup.cfg index 22c367a..8c98311 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = chainqueue -version = 0.1.1 +version = 0.1.2 description = Generic blockchain transaction queue control author = Louis Holbrook author_email = dev@holbrook.no