Use __init_subclass__ to register storage backends

See https://docs.python.org/3.6/whatsnew/3.6.html#pep-487-simpler-customization-of-class-creation
This commit is contained in:
2017-03-21 16:04:40 +01:00
parent 1f3d699a0c
commit 6eadc09c10
2 changed files with 21 additions and 20 deletions

View File

@@ -9,7 +9,7 @@ from gcloud.exceptions import NotFound
from flask import current_app, g from flask import current_app, g
from werkzeug.local import LocalProxy from werkzeug.local import LocalProxy
from pillar.api.utils.storage import register_backend, Bucket, Blob from pillar.api.utils.storage import Bucket, Blob
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -34,7 +34,6 @@ def get_client():
gcs = LocalProxy(get_client) gcs = LocalProxy(get_client)
@register_backend('gcs')
class GoogleCloudStorageBucket(Bucket): class GoogleCloudStorageBucket(Bucket):
"""Cloud Storage bucket interface. We create a bucket for every project. In """Cloud Storage bucket interface. We create a bucket for every project. In
the bucket we create first level subdirs as follows: the bucket we create first level subdirs as follows:
@@ -51,6 +50,8 @@ class GoogleCloudStorageBucket(Bucket):
""" """
backend_name = 'gcs'
def __init__(self, name, subdir='_/'): def __init__(self, name, subdir='_/'):
super(GoogleCloudStorageBucket, self).__init__(name=name) super(GoogleCloudStorageBucket, self).__init__(name=name)
try: try:

View File

@@ -14,18 +14,6 @@ from pillar.api.utils.imaging import generate_local_thumbnails
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# Mapping from backend name to backend class
backends = {}
def register_backend(backend_name):
def wrapper(cls):
assert backend_name not in backends
backends[backend_name] = cls
return cls
return wrapper
class Bucket(object, metaclass=abc.ABCMeta): class Bucket(object, metaclass=abc.ABCMeta):
"""Can be a GCS bucket or simply a project folder in Pillar """Can be a GCS bucket or simply a project folder in Pillar
@@ -36,9 +24,24 @@ class Bucket(object, metaclass=abc.ABCMeta):
""" """
# Mapping from backend name to Bucket class
backends = {}
backend_name: str = None # define in subclass.
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
assert cls.backend_name, '%s.backend_name must be non-empty string' % cls
cls.backends[cls.backend_name] = cls
@classmethod
def for_backend(cls, backend_name: str) -> type:
"""Returns the Bucket subclass for the given backend."""
return cls.backends[backend_name]
@abc.abstractmethod @abc.abstractmethod
def blob(self, blob_name): def blob(self, blob_name):
"""Factory constructor for blob object. """Factory constructor for blob object.
@@ -175,10 +178,8 @@ class Blob(object, metaclass=abc.ABCMeta):
file_id, status, r) file_id, status, r)
@register_backend('local')
class LocalBucket(Bucket): class LocalBucket(Bucket):
def __init__(self, name): backend_name = 'local'
super(LocalBucket, self).__init__(name=name)
def blob(self, blob_name): def blob(self, blob_name):
return LocalBlob(name=blob_name, bucket=self) return LocalBlob(name=blob_name, bucket=self)
@@ -259,7 +260,6 @@ class LocalBlob(Blob):
def default_storage_backend(name): def default_storage_backend(name):
from flask import current_app from flask import current_app
backend_cls = backends[current_app.config['STORAGE_BACKEND']] backend_name = current_app.config['STORAGE_BACKEND']
backend_cls = Bucket.for_backend(backend_name)
return backend_cls(name) return backend_cls(name)