Add initial support for rattail "providers"

so integration projects etc. can supplement the main AppHandler
without subclassing it
This commit is contained in:
Lance Edgar 2023-09-14 12:55:27 -05:00
parent 08c5da5de3
commit 69ff5f73db

View file

@ -84,13 +84,31 @@ class AppHandler(object):
def __init__(self, config):
self.config = config
self.enum = self.config.get_enum()
# app may not use the db layer, but if so we set the model
try:
self.model = config.get_model()
self.model = self.config.get_model()
except: # pragma: no cover
pass
self.handlers = {}
self.providers = self.get_all_providers()
def get_all_providers(self):
"""
Returns a dict of all registered providers.
"""
providers = load_entry_points('rattail.providers')
for key in list(providers):
providers[key] = providers[key](self)
return providers
def __getattr__(self, name):
for provider in self.providers.values():
if hasattr(provider, name):
return getattr(provider, name)
def get_title(self, default=None):
"""
Returns the configured title (name) of the app.
@ -532,12 +550,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.auth.AuthHandler` instance for
the app.
"""
if not hasattr(self, 'auth_handler'):
if 'auth' not in self.handlers:
spec = self.config.get('rattail', 'auth.handler',
default='rattail.auth:AuthHandler')
factory = load_object(spec)
self.auth_handler = factory(self.config, **kwargs)
return self.auth_handler
self.handlers['auth'] = factory(self.config, **kwargs)
return self.handlers['auth']
def get_batch_handler(self, key, default=None, error=True, **kwargs):
"""
@ -585,10 +603,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.board.BoardHandler` instance
for the app.
"""
if not hasattr(self, 'board_handler'):
if 'board' not in self.handlers:
from rattail.board import get_board_handler
self.board_handler = get_board_handler(self.config, **kwargs)
return self.board_handler
self.handlers['board'] = get_board_handler(self.config, **kwargs)
return self.handlers['board']
def get_bounce_handler(self, key, **kwargs):
"""
@ -601,10 +619,11 @@ class AppHandler(object):
instance of the requested type. If no such handler can be
found, an error will raise.
"""
if not hasattr(self, 'bounce_handlers'):
self.bounce_handlers = {}
if 'bounce' not in self.handlers:
self.handlers['bounce'] = {}
handlers = self.handlers['bounce']
if key not in self.bounce_handlers:
if key not in handlers:
spec = self.config.get('rattail.bouncer',
'{}.handler'.format(key))
if not spec and key == 'default':
@ -614,9 +633,9 @@ class AppHandler(object):
"type: {}".format(key))
Handler = self.load_object(spec)
self.bounce_handlers[key] = Handler(self.config, key)
handlers[key] = Handler(self.config, key)
return self.bounce_handlers[key]
return handlers[key]
def get_cleanup_handler(self, **kwargs):
"""
@ -625,12 +644,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.cleanup.CleanupHandler`
instance for the app.
"""
if not hasattr(self, 'cleanup_handler'):
if 'cleanup' not in self.handlers:
spec = self.config.get('rattail.cleanup', 'handler',
default='rattail.cleanup:CleanupHandler')
Handler = self.load_object(spec)
self.cleanup_handler = Handler(self.config)
return self.cleanup_handler
self.handlers['cleanup'] = Handler(self.config)
return self.handlers['cleanup']
def get_clientele_handler(self, **kwargs):
"""
@ -639,10 +658,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.clientele.ClienteleHandler`
instance for the app.
"""
if not hasattr(self, 'clientele_handler'):
if 'clientele' not in self.handlers:
from rattail.clientele import get_clientele_handler
self.clientele_handler = get_clientele_handler(self.config, **kwargs)
return self.clientele_handler
self.handlers['clientele'] = get_clientele_handler(self.config, **kwargs)
return self.handlers['clientele']
def get_custorder_handler(self, **kwargs):
"""
@ -651,12 +670,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.custorders.CustomerOrderHandler`
instance for the app.
"""
if not hasattr(self, 'custorder_handler'):
if 'custorder' not in self.handlers:
spec = self.config.get('rattail', 'custorders.handler',
default='rattail.custorders:CustomerOrderHandler')
Handler = self.load_object(spec)
self.custorder_handler = Handler(self.config)
return self.custorder_handler
self.handlers['custorder'] = Handler(self.config)
return self.handlers['custorder']
def get_datasync_handler(self, **kwargs):
"""
@ -666,12 +685,12 @@ class AppHandler(object):
:class:`~rattail.datasync.handler.DatasyncHandler` instance
for the app.
"""
if not hasattr(self, 'datasync_handler'):
if 'datasync' not in self.handlers:
spec = self.config.get('rattail.datasync', 'handler',
default='rattail.datasync.handler:DatasyncHandler')
Handler = self.load_object(spec)
self.datasync_handler = Handler(self.config, **kwargs)
return self.datasync_handler
self.handlers['datasync'] = Handler(self.config, **kwargs)
return self.handlers['datasync']
def get_db_handler(self, **kwargs):
"""
@ -680,12 +699,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.db.handler.DatabaseHandler`
instance for the app.
"""
if not hasattr(self, 'db_handler'):
if 'db' not in self.handlers:
spec = self.config.get('rattail.db', 'handler',
default='rattail.db.handler:DatabaseHandler')
Handler = self.load_object(spec)
self.db_handler = Handler(self.config)
return self.db_handler
self.handlers['db'] = Handler(self.config)
return self.handlers['db']
def get_employment_handler(self, **kwargs):
"""
@ -694,10 +713,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.employment.EmploymentHandler`
instance for the app.
"""
if not hasattr(self, 'employment_handler'):
if 'employment' not in self.handlers:
from rattail.employment import get_employment_handler
self.employment_handler = get_employment_handler(self.config, **kwargs)
return self.employment_handler
self.handlers['employment'] = get_employment_handler(self.config, **kwargs)
return self.handlers['employment']
def get_feature_handler(self, **kwargs):
"""
@ -706,10 +725,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.features.handlers.FeatureHandler`
instance for the app.
"""
if not hasattr(self, 'feature_handler'):
if 'feature' not in self.handlers:
from rattail.features import FeatureHandler
self.feature_handler = FeatureHandler(self.config, **kwargs)
return self.feature_handler
self.handlers['feature'] = FeatureHandler(self.config, **kwargs)
return self.handlers['feature']
def get_email_handler(self, **kwargs):
"""
@ -718,7 +737,7 @@ class AppHandler(object):
:returns: The :class:`~rattail.mail.EmailHandler` instance for
the app.
"""
if not hasattr(self, 'email_handler'):
if 'email' not in self.handlers:
spec = self.config.get('rattail.mail', 'handler')
if not spec:
spec = self.config.get('rattail', 'email.handler')
@ -729,8 +748,8 @@ class AppHandler(object):
else:
spec = 'rattail.mail:EmailHandler'
Handler = self.load_object(spec)
self.email_handler = Handler(self.config, **kwargs)
return self.email_handler
self.handlers['email'] = Handler(self.config, **kwargs)
return self.handlers['email']
def get_mail_handler(self, **kwargs): # pragma: no cover
warnings.warn("method is deprecated, please use "
@ -937,12 +956,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.labels.LabelHandler` instance
for the app.
"""
if not hasattr(self, 'label_handler'):
if 'label' not in self.handlers:
spec = self.config.get('rattail', 'labels.handler',
default='rattail.labels:LabelHandler')
factory = self.load_object(spec)
self.label_handler = factory(self.config, **kwargs)
return self.label_handler
self.handlers['label'] = factory(self.config, **kwargs)
return self.handlers['label']
def get_luigi_handler(self, **kwargs):
"""
@ -951,12 +970,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.luigi.handler.LuigiHandler`
instance for the app.
"""
if not hasattr(self, 'luigi_handler'):
if 'luigi' not in self.handlers:
spec = self.config.get('rattail.luigi', 'handler',
default='rattail.luigi.handler:LuigiHandler')
Handler = self.load_object(spec)
self.luigi_handler = Handler(self.config, **kwargs)
return self.luigi_handler
self.handlers['luigi'] = Handler(self.config, **kwargs)
return self.handlers['luigi']
def get_membership_handler(self, **kwargs):
"""
@ -967,12 +986,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.membership.MembershipHandler`
instance for the app.
"""
if not hasattr(self, 'membership_handler'):
if 'membership' not in self.handlers:
spec = self.config.get('rattail', 'membership.handler',
default='rattail.membership:MembershipHandler')
factory = load_object(spec)
self.membership_handler = factory(self.config, **kwargs)
return self.membership_handler
self.handlers['membership'] = factory(self.config, **kwargs)
return self.handlers['membership']
def get_org_handler(self, **kwargs):
"""
@ -981,12 +1000,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.org.OrgHandler` instance for
the app.
"""
if not hasattr(self, 'org_handler'):
if 'org' not in self.handlers:
spec = self.config.get('rattail', 'org.handler',
default='rattail.org:OrgHandler')
factory = load_object(spec)
self.org_handler = factory(self.config, **kwargs)
return self.org_handler
self.handlers['org'] = factory(self.config, **kwargs)
return self.handlers['org']
def get_people_handler(self, **kwargs):
"""
@ -997,12 +1016,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.people.PeopleHandler` instance
for the app.
"""
if not hasattr(self, 'people_handler'):
if 'people' not in self.handlers:
spec = self.config.get('rattail', 'people.handler',
default='rattail.people:PeopleHandler')
factory = load_object(spec)
self.people_handler = factory(self.config, **kwargs)
return self.people_handler
self.handlers['people'] = factory(self.config, **kwargs)
return self.handlers['people']
def get_poser_handler(self, **kwargs):
"""
@ -1011,12 +1030,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.poser.PoserHandler` instance
for the app.
"""
if not hasattr(self, 'poser_handler'):
if 'poser' not in self.handlers:
spec = self.config.get('rattail', 'poser.handler',
default='rattail.poser:PoserHandler')
factory = self.load_object(spec)
self.poser_handler = factory(self.config, **kwargs)
return self.poser_handler
self.handlers['poser'] = factory(self.config, **kwargs)
return self.handlers['poser']
def get_products_handler(self, **kwargs):
"""
@ -1025,10 +1044,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.products.ProductsHandler`
instance for the app.
"""
if not hasattr(self, 'products_handler'):
if 'products' not in self.handlers:
from rattail.products import get_products_handler
self.products_handler = get_products_handler(self.config, **kwargs)
return self.products_handler
self.handlers['products'] = get_products_handler(self.config, **kwargs)
return self.handlers['products']
def get_report_handler(self, **kwargs):
"""
@ -1037,10 +1056,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.reporting.handlers.ReportHandler`
instance for the app.
"""
if not hasattr(self, 'report_handler'):
if 'report' not in self.handlers:
from rattail.reporting import get_report_handler
self.report_handler = get_report_handler(self.config, **kwargs)
return self.report_handler
self.handlers['report'] = get_report_handler(self.config, **kwargs)
return self.handlers['report']
def get_problem_report_handler(self, **kwargs):
"""
@ -1049,11 +1068,10 @@ class AppHandler(object):
:returns: The :class:`~rattail.problems.handlers.ProblemReportHandler`
instance for the app.
"""
if not hasattr(self, 'problem_report_handler'):
if 'problem_report' not in self.handlers:
from rattail.problems import get_problem_report_handler
self.problem_report_handler = get_problem_report_handler(
self.config, **kwargs)
return self.problem_report_handler
self.handlers['problem_report'] = get_problem_report_handler(self.config, **kwargs)
return self.handlers['problem_report']
def get_project_handler(self, **kwargs):
"""
@ -1062,12 +1080,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.projects.handler.ProjectHandler`
instance for the app.
"""
if not hasattr(self, 'project_handler'):
if 'project' not in self.handlers:
spec = self.config.get('project', 'handler',
default='rattail.projects.handler:ProjectHandler')
Handler = self.load_object(spec)
self.project_handler = Handler(self.config)
return self.project_handler
self.handlers['project'] = Handler(self.config)
return self.handlers['project']
def get_tailbone_handler(self, **kwargs):
"""
@ -1076,12 +1094,12 @@ class AppHandler(object):
:returns: The :class:`~tailbone:tailbone.handler.TailboneHandler`
instance for the app.
"""
if not hasattr(self, 'tailbone_handler'):
if 'tailbone' not in self.handlers:
spec = self.config.get('tailbone', 'handler',
default='tailbone.handler:TailboneHandler')
Handler = self.load_object(spec)
self.tailbone_handler = Handler(self.config)
return self.tailbone_handler
self.handlers['tailbone'] = Handler(self.config)
return self.handlers['tailbone']
def get_telemetry_handler(self, **kwargs):
"""
@ -1090,12 +1108,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.telemetry.handler.TelemetryHandler`
instance for the app.
"""
if not hasattr(self, 'telemetry_handler'):
if 'telemetry' not in self.handlers:
spec = self.config.get('rattail.telemetry', 'handler',
default='rattail.telemetry:TelemetryHandler')
Handler = self.load_object(spec)
self.telemetry_handler = Handler(self.config)
return self.telemetry_handler
self.handlers['telemetry'] = Handler(self.config)
return self.handlers['telemetry']
def get_trainwreck_handler(self, **kwargs):
"""
@ -1104,12 +1122,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.trainwreck.handler.TrainwreckHandler`
instance for the app.
"""
if not hasattr(self, 'trainwreck_handler'):
if 'trainwreck' not in self.handlers:
spec = self.config.get('trainwreck', 'handler',
default='rattail.trainwreck.handler:TrainwreckHandler')
Handler = self.load_object(spec)
self.trainwreck_handler = Handler(self.config)
return self.trainwreck_handler
self.handlers['trainwreck'] = Handler(self.config)
return self.handlers['trainwreck']
def get_upgrade_handler(self, **kwargs):
"""
@ -1118,7 +1136,7 @@ class AppHandler(object):
:returns: The :class:`~rattail.upgrades.UpgradeHandler`
instance for the app.
"""
if not hasattr(self, 'upgrade_handler'):
if 'upgrade' not in self.handlers:
default = 'rattail.upgrades:UpgradeHandler'
# nb. previous get_upgrade_handler() function accepted
# a 'default' kwarg, so i guess we still do here too
@ -1131,8 +1149,8 @@ class AppHandler(object):
spec = self.config.get('rattail.upgrades', 'handler',
default=default)
Handler = self.load_object(spec)
self.upgrade_handler = Handler(self.config)
return self.upgrade_handler
self.handlers['upgrade'] = Handler(self.config)
return self.handlers['upgrade']
def get_vendor_handler(self, **kwargs):
"""
@ -1141,12 +1159,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.vendors.handler.VendorHandler`
instance for the app.
"""
if not hasattr(self, 'vendor_handler'):
if 'vendor' not in self.handlers:
spec = self.config.get('rattail', 'vendors.handler',
default='rattail.vendors:VendorHandler')
factory = self.load_object(spec)
self.vendor_handler = factory(self.config, **kwargs)
return self.vendor_handler
self.handlers['vendor'] = factory(self.config, **kwargs)
return self.handlers['vendor']
def get_workorder_handler(self, **kwargs):
"""
@ -1155,12 +1173,12 @@ class AppHandler(object):
:returns: The :class:`~rattail.workorders.WorkOrderHandler`
instance for the app.
"""
if not hasattr(self, 'workorder_handler'):
if 'workorder' not in self.handlers:
spec = self.config.get('rattail', 'workorders.handler',
default='rattail.workorders:WorkOrderHandler')
Handler = self.load_object(spec)
self.workorder_handler = Handler(self.config)
return self.workorder_handler
self.handlers['workorder'] = Handler(self.config)
return self.handlers['workorder']
def progress_loop(self, *args, **kwargs):
"""
@ -2044,6 +2062,32 @@ class MergeMixin(object):
setattr(keeping, field['name'], removing_value)
class RattailProvider:
"""
Base class for Rattail providers. These can add arbitrary extra
functionality to the main AppHandler.
"""
def __init__(self, app):
self.app = app
self.config = app.config
self.model = app.model
self.enum = app.enum
def load_object(self, *args, **kwargs):
return self.app.load_object(*args, **kwargs)
def get_all_providers(config):
"""
Returns a dict of all registered providers.
"""
providers = load_entry_points('rattail.providers')
for key in list(providers):
providers[key] = providers[key](config)
return providers
def make_app(config, **kwargs): # pragma: no cover
warnings.warn("function is deprecated, please use "
"RattailConfig.get_app() method instead",