Made AbstractConsensusRules use python's abc and enforcing that ConsensusRules plugins subclass it

This commit is contained in:
Matt Smith 2016-03-08 18:32:25 -08:00
parent 01d706ac56
commit 14b71537d6
3 changed files with 49 additions and 30 deletions

View File

@ -41,9 +41,7 @@ config = {
'rate': e('BIGCHAIN_STATSD_SAMPLERATE', default=0.01)
},
'api_endpoint': 'http://localhost:8008/api/v1',
'consensus_plugins': [
'base'
]
'consensus_plugin': e('BIGCHAIN_CONSENSUS_PLUGIN', default='default')
}
# We need to maintain a backup copy of the original config dict in case

View File

@ -17,7 +17,7 @@ import json
import logging
import collections
from pkg_resources import iter_entry_points
from pkg_resources import iter_entry_points, ResolutionError
import bigchaindb
@ -103,20 +103,37 @@ def autoconfigure():
logger.warning('Cannot find your config file. Run `bigchaindb configure` to create one')
def get_plugins(plugin_names):
if not plugin_names:
plugin_names = bigchaindb.config.get('consensus_plugins', [])
def load_consensus_plugin(name=None):
"""Find and load the chosen consensus plugin.
plugins = []
Args:
name (string): the name of the entry_point, as advertised in the
setup.py of the providing package.
# It's important to maintain plugin ordering as stated in the config file.
# e.g. Expensive validation tasks should happen after cheap ones.
#
# TODO: We might want to add some sort of priority system, but for now we
# simply assume everything in a given plugin is designed to run at the
# same time.
for name in plugin_names:
for entry_point in iter_entry_points('bigchaindb.plugins', name):
plugins.append(entry_point.load())
Returns:
an uninstantiated subclass of ``bigchaindb.consensus.AbstractConsensusRules``
"""
if not name:
name = bigchaindb.config.get('consensus_plugin', 'default')
return plugins
# TODO: This will return the first plugin with group `bigchaindb.plugins`
# and name `name` in the active WorkingSet.
# We should probably support Requirements specs in the config, e.g.
# consensus_plugin: 'my-plugin-package==0.0.1;default'
plugin = None
for entry_point in iter_entry_points('bigchaindb.consensus', name):
plugin = entry_point.load()
# No matching entry_point found
if not plugin:
raise ResolutionError(
'No plugin found in group `bigchaindb.plugins` with name `{}`'.
format(name))
# Is this strictness desireable?
# It will probably reduce developer headaches in the wild.
if not issubclass(plugin, (AbstractConsensusRules)):
raise TypeError("object of type '{}' does not implement `bigchaindb."
"consensus.AbstractConsensusRules`".format(type(plugin)))
return plugin

View File

@ -1,17 +1,21 @@
from abc import ABCMeta, abstractmethod
import bigchaindb.exceptions as exceptions
from bigchaindb import util
from bigchaindb.crypto import hash_data
from bigchaindb.crypto import hash_data, PublicKey
# TODO: no real reason to use abc yet, but later we can enforce inheritance from
# this class when loading plugins if that's desirable.
# from abc import ABCMeta
class AbstractConsensusRules:
class AbstractConsensusRules(metaclass=ABCMeta):
"""Abstract base class for Bigchain plugins which implement consensus logic.
# TODO: rather than having plugin-authors inherit and override,
# it'd be cleaner to make a `transactionrule` decorator and etc
@classmethod
def validate_transaction(cls, bigchain, transaction):
A consensus plugin must expose a class inheriting from this one via an
entry_point.
All methods listed below must be implemented.
"""
@abstractmethod
def validate_transaction(bigchain, transaction):
"""Validate a transaction.
Args:
@ -26,10 +30,10 @@ class AbstractConsensusRules:
Descriptive exceptions indicating the reason the transaction failed.
See the `exceptions` module for bigchain-native error classes.
"""
return transaction
raise NotImplementedError
@classmethod
def validate_block(cls, bigchain, block):
@abstractmethod
def validate_block(bigchain, block):
"""Validate a block.
Args: