From 53ff23d7641ad93117087854e88e29f1c1dac011 Mon Sep 17 00:00:00 2001 From: Joel Dunham Date: Thu, 8 Mar 2018 10:22:27 -0800 Subject: [PATCH] Reduce dependency declaration verbosity Use metaprogramming so that dependencies can be declared without redundantly specifying the name of the dependency. Fixes #38 --- metsrw/di.py | 28 +++++++++++++++++++++++++--- metsrw/fsentry.py | 13 ++++++++----- tests/test_dependency_injection.py | 1 - 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/metsrw/di.py b/metsrw/di.py index 7817f79..f9d5bb5 100644 --- a/metsrw/di.py +++ b/metsrw/di.py @@ -19,6 +19,8 @@ See http://code.activestate.com/recipes/413268/ """ +from six import with_metaclass + from .plugins import premisrw @@ -81,15 +83,14 @@ class Dependency(object): >>> from .di import is_class, has_methods, Dependency >>> class FSEntry(object): ... premis_object_class = Dependency( - ... 'premis_object_class', ... has_methods('serialize'), ... has_class_methods('fromtree'), ... is_class) """ - def __init__(self, dependency_name, *assertions): - self.dependency_name = dependency_name + def __init__(self, *assertions): + self.dependency_name = None self.assertions = assertions def __get__(self, instance, owner): @@ -101,6 +102,27 @@ def __get__(self, instance, owner): return obj +class DependencyPossessorMeta(type): + """Metaclass for ``DependencyPossessor``. Classes that can have + dependencies---i.e., classes that have class attributes whose values are + instances of ``Dependency`` above---must inherit from + ``DependencyPossessor``. This allows us to tell the ``Dependency`` instance + that its ``dependency_name`` value should be the same as the name of the + class attribute it was assigned to in the managed class. In short, it + allows us to write ``premis_object_class = Dependency()`` instead of + ``premis_object_class = Dependency('premis_object_class')``. + """ + def __init__(cls, name, bases, attr_dict): + super(DependencyPossessorMeta, cls).__init__(name, bases, attr_dict) + for key, attr in attr_dict.items(): + if isinstance(attr, Dependency): + attr.dependency_name = key + + +class DependencyPossessor(with_metaclass(DependencyPossessorMeta, object)): + pass + + # ============================================================================== # Assertions for declaring dependencies using Dependency # ============================================================================== diff --git a/metsrw/fsentry.py b/metsrw/fsentry.py index 2c621c2..2fe843b 100644 --- a/metsrw/fsentry.py +++ b/metsrw/fsentry.py @@ -8,7 +8,13 @@ from lxml import etree import six -from .di import is_class, has_methods, has_class_methods, Dependency +from .di import ( + is_class, + has_methods, + has_class_methods, + Dependency, + DependencyPossessor, +) from . import exceptions from .metadata import MDWrap, MDRef, SubSection, AMDSec from . import utils @@ -16,7 +22,7 @@ LOGGER = logging.getLogger(__name__) -class FSEntry(object): +class FSEntry(DependencyPossessor): """A class representing a filesystem entry - either a file or a directory. When passed to a :class:`metsrw.mets.METSDocument` instance, the tree of @@ -75,17 +81,14 @@ class FSEntry(object): # ``FSEntry`` instance must be able to call ``self.premis_object_class`` and # get a class with methods ``fromtree`` and ``serialize``. premis_object_class = Dependency( - 'premis_object_class', has_methods('serialize'), has_class_methods('fromtree'), is_class) premis_event_class = Dependency( - 'premis_event_class', has_methods('serialize'), has_class_methods('fromtree'), is_class) premis_agent_class = Dependency( - 'premis_agent_class', has_methods('serialize'), has_class_methods('fromtree'), is_class) diff --git a/tests/test_dependency_injection.py b/tests/test_dependency_injection.py index b65b9cc..aa5989c 100644 --- a/tests/test_dependency_injection.py +++ b/tests/test_dependency_injection.py @@ -142,7 +142,6 @@ def test_dependency_injection(self): with ``fromtree`` and ``serialize`` methods:: >>> premis_object_class = Dependency( - ... 'premis_object_class', ... has_methods('serialize'), ... has_class_methods('fromtree'), ... is_class)