Skip to content

Commit

Permalink
Reduce dependency declaration verbosity
Browse files Browse the repository at this point in the history
Use metaprogramming so that dependencies can be declared without
redundantly specifying the name of the dependency.

Fixes #38
  • Loading branch information
jrwdunham committed Mar 8, 2018
1 parent 21438f2 commit 53ff23d
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
28 changes: 25 additions & 3 deletions metsrw/di.py
Expand Up @@ -19,6 +19,8 @@
See http://code.activestate.com/recipes/413268/
"""

from six import with_metaclass

from .plugins import premisrw


Expand Down Expand Up @@ -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):
Expand All @@ -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
# ==============================================================================
Expand Down
13 changes: 8 additions & 5 deletions metsrw/fsentry.py
Expand Up @@ -8,15 +8,21 @@
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

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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion tests/test_dependency_injection.py
Expand Up @@ -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)
Expand Down

0 comments on commit 53ff23d

Please sign in to comment.