aglyph.binder — The Aglyph component binder

Release:2.1.1

The Aglyph binder provides a concise programmatic-configuration option for Aglyph.

New in version 1.1.0.

The Aglyph binder provides a much simpler and more compact alternative to aglyph.assembler.Assembler, aglyph.context.Context, and aglyph.component.Component for programmatic configuration of Aglyph.

“Binding” usually results in less configuration code. A Binder also offers the same functionality as an aglyph.assembler.Assembler, so the same object used to configure injection can be used to perform injection, further reducing the amount of “bootstrap” code needed.

For example, the following two blocks exhibit identical behavior:

Using Context, Component, and Assembler directly:

context = Context("context-id")
component = Component("my-class", dotted_name="my.package.MyClass")
component.init_args.append("value")
component.init_keywords["foo"] = "bar"
component.attributes["set_spam"] = "eggs"
context.add(component)
assembler = Assembler(context)
...
my_object = assembler.assemble("my-class")

Using Binder:

binder = Binder()
(binder.bind("my-class", to="my.package.MyClass").
    init("value", foo="bar").
    attributes(set_spam="eggs"))
...
my_object = binder.lookup("my-class")
class aglyph.binder.Binder(binder_id=None, after_inject=None, before_clear=None)[source]

Bases: aglyph.assembler.Assembler

Configure and assemble application components.

Parameters:
  • binder_id (str) – a unique identifier for this binder
  • after_inject (str) – specifies the name of the method that will be called on assembled objects after all of their dependencies have been injected
  • before_clear (str) – specifies the name of the method that will be called on assembled objects immediately before they are cleared from cache

If binder_id is not provided, a random identifier is generated.

New in version 2.1.0: the after_inject and before_clear keyword arguments

binder_id

The unique binder identifier (read-only).

bind(component_spec, to=None, factory=None, member=None, strategy='prototype', parent=None, after_inject=None, before_clear=None)[source]

Define a component by associating the unique identifier for component_spec with the importable dotted-name for to.

Parameters:
  • component_spec – used to determine the value that will serve as the lookup() key for objects of the component
  • to – used to identify the class or function that will be called to initialize objects of the component
  • factory (str) – names a callable member of the object identified by the component_spec argument or to keyword
  • member (str) – names any member of the object identified by the component_spec argument or to keyword
  • strategy (str) – specifies the component assembly strategy
  • parent (str) – specifies the ID of a template or component that describes the default dependencies and/or lifecyle methods for this component
  • after_inject (str) – specifies the name of the method that will be called on objects of this component after all of its dependencies have been injected
  • before_clear (str) – specifies the name of the method that will be called on objects of this component immediately before they are cleared from cache
Returns:

a proxy object that allows the component dependencies to be defined in chained-call fashion

Return type:

aglyph.binder._DependencySupportProxy

If component_spec is a str, it is used as the unique identifier for the component. In this case, the to keyword is required.

Otherwise, component_spec must be an importable class, unbound function, or module. Its dotted name will be used as the component identifier.

If to is not provided, the component identifier will be used as both the component’s identifier and dotted name. In such cases, component_spec must be either an importable class, unbound function, or module, or a dotted name.

New in version 2.0.0: the factory keyword argument

factory is the name of a callable member of the importable object (i.e. factory is a function, class, staticmethod, or classmethod). When provided, this member is called to assemble the component.

factory enables Aglyph to inject dependencies into objects that can only be initialized via nested classes, staticmethod, or classmethod. For example:

# in module 'module.py'
class Outer:
    class Nested:
        pass

# elsewhere...
binder.bind("nested-instance", to="module.Outer",
            factory="Nested")

When assembled, the “nested-instance” component will be an instance of the module.Outer.Nested class.

New in version 2.0.0: the member keyword argument

member is the name of any member of the importable object (including a callable member, such as a nested class).

member differs from factory in two ways:

  1. member is not restricted to callable members; it may name an attribute or property, as well as a callable.
  2. When the binder looks up a component with a defined member, initialization is bypassed (i.e. no initialization dependencies are injected).

member enables Aglyph to reference classes, functions, staticmethods, classmethods, and attributes as dependencies. For example:

# in module 'module.py'
class Outer:
    class Nested:
        pass

# elsewhere...
binder.bind("nested-class", to="module.Outer",
            member="Nested")

When assembled, the “nested-class” component will be the module.Outer.Nested class object itself.

Note

Both factory and member can be dot-separated names to reference nested members.

Warning

The factory and member arguments are mutually exclusive. An exception is raised if both are provided.

strategy must be a recognized component assembly strategy, and defaults to aglyph.component.Strategy.PROTOTYPE (“prototype”) if not specified.

Please see aglyph.component.Strategy for a description of the component assembly strategies supported by Aglyph.

Warning

The aglyph.component.Strategy.BORG (“borg”) component assembly strategy is only supported for classes that do not define or inherit __slots__!

New in version 2.1.0: the parent keyword argument

If parent is a str, it is used as the unique identifier for the component’s parent template or component.

If parent is not a string, then it must be an importable class, unbound function, or module. Its dotted name will be used as the Component.parent_id.

New in version 2.1.0: the after_inject keyword argument

after_inject is the name of a method of objects of this component that will be called after all dependencies have been injected, but before the object is returned to the caller. This method will be called with no arguments (positional or keyword). Exceptions raised by this method are not caught.

Note

Component.after_inject, if specified, replaces either Template.after_inject (if this component also specifies parent_id) or aglyph.context.Context.after_inject.

New in version 2.1.0: the before_clear keyword argument

before_clear is the name of a method of objects of this component that will be called immediately before the object is cleared from cache via aglyph.assembler.Assembler.clear_singletons(), aglyph.assembler.Assembler.clear_borgs(), or aglyph.assembler.Assembler.clear_weakrefs().

Note

Component.before_clear, if specified, replaces either Template.before_clear (if this component also specifies parent_id) or aglyph.context.Context.before_clear.

Warning

The before_clear keyword argument has no meaning for and is ignored by “prototype” components. If before_clear is specified for a prototype, a RuntimeWarning will be issued.

For “weakref” components, there is a possibility that the object no longer exists at the moment when the before_clear method would be called. In such cases, the before_clear method is not called. No warning is issued, but a logging.WARNING message is emitted.

describe(template_spec, parent=None, after_inject=None, before_clear=None)[source]

Define a template using the unique identifier derived from template_spec.

Parameters:
  • template_spec – used to determine the value that will serve as the unique identifier for the template
  • parent (str) – specifies the ID of a template or component that describes the default dependencies and/or lifecyle methods for this template
  • after_inject (str) – specifies the name of the method that will be called on objects of components that refer to this template’s ID as the Component.parent_id immediately after all component dependencies have been injected
  • before_clear (str) – specifies the name of the method that will be called on objects of components that refer to this template’s ID as the Component.parent_id when the objects are removed from any internal cache
Returns:

a proxy object that allows the template dependencies to be defined in chained-call fashion

Return type:

aglyph.binder._DependencySupportProxy

If template_spec is a str, it is used as the unique identifier for the template. Otherwise, template_spec must be an importable class, unbound function, or module. Its dotted name will be used as the template identifier.

If parent is a str, it is used as the unique identifier for the component’s parent template or component.

If parent is not a string, then it must be an importable class, unbound function, or module. Its dotted name will be used as the Component.parent_id.

after_inject is the name of a method (of objects of components that reference this template) that will be called after all dependencies have been injected, but before the object is returned to the caller.

The method named by after_inject will be called with no arguments (positional or keyword). Exceptions raised by this method are ignored (though a RuntimeWarning will be issued).

Note

Template.after_inject, if specified, replaces any after-inject method named by its parent or by aglyph.context.Context.after_inject.

before_clear is the name of a method (of objects of components that reference this template) that will be called when the object is cleared from cache via Assembler.clear_singletons(), Assembler.clear_borgs(), or Assembler.clear_weakrefs().

Note

Template.before_clear, if specified, replaces any before-clear method named by its parent or by aglyph.context.Context.before_clear.

Warning

The before_clear keyword argument has no meaning for and is ignored by “prototype” components. If before_clear is specified for a prototype, a RuntimeWarning will be issued.

For “weakref” components, there is a possibility that the object no longer exists at the moment when the before_clear method would be called. In such cases, the before_clear method is not called. No warning is issued, but a logging.WARNING message is emitted.

lookup(component_spec)[source]

Return an instance of the component specified by component_spec with all of its dependencies provided.

component_spec must be an importable class or unbound function, or a user-defined unique identifier, that was previously bound by a call to bind().

Deprecated since version 2.1.0: use assemble() instead.

class aglyph.binder._DependencySupportProxy(depsupport)[source]

Bases: object

Instances of __DependencySupportProxy are returned as proxy objects from the Binder.bind() and Binder.describe() methods.

A _DependencySupportProxy allows component dependencies to be defined in chained-call fashion:

binder = Binder()
binder.bind(...).init(*args, **keywords).attributes(**keywords)
# - or -
(binder.describe(...).
    init(*args, **keywords).
    attributes(**keywords))

Note

This class should not be imported or otherwise directly referenced.

Parameters:depsupport – a Component or Template that was created by Binder.bind() or Binder.describe(), respectively
init(*args, **keywords)[source]

Define the initialization dependencies (i.e. positional and/or keyword arguments) for the component or template.

Parameters:
  • args (tuple) – the positional argument dependencies
  • keywords (dict) – the keyword argument dependencies
Returns:

a reference to self (enables chained calls)

If any argument value is a class, unbound function, or module type, it is automatically turned into a Reference:

# SomeClass becomes
# ``Reference(format_dotted_name(SomeClass))``
binder.bind("thing", to="module.Thing").init(SomeClass)

Warning

In the above example, there must exist a component that has the ID “package.module.SomeClass”:

binder.bind(SomeClass).init(...).attributes(...)
# - or -
(binder.bind("package.module.SomeClass").
    init(...).attributes(...))

If more flexibility is needed, simply create and pass in an explicit Reference:

(binder.bind("something", to=SomeClass).
    init(...).attributes(...))
...
(binder.bind("thing", to="module.Thing").
    init(Reference("something")))

Changed in version 2.0.0: Successive calls to this method on the same instance have a cumulative effect.

attributes(*nvpairs, **keywords)[source]

Define the setter dependencies (i.e. attributes, setter methods, and/or properties) for the component.

Parameters:
  • nvpairs (tuple) – an ordered N-tuple of (name, value) 2-tuples that describe the attribute, setter method, and/or property dependencies
  • keywords (dict) – the attribute, setter method, and/or property dependencies
Returns:

a reference to self (enables chained calls)

New in version 2.1.0::: the nvpairs parameter.

If the order in which attribute/setter/property dependencies are injected is significant, use nvpairs to control the order; otherwise, if order of injection doesn’t matter, it is simpler to use keywords.

In each (name, value) 2-tuple of nvpairs, name corresponds to the name of a simple attribute, setter method, or property:

(binder.bind("thing", to="module.Thing").
    attributes(("id", 1), ("set_name", "First thing")))

Each key in keywords corresponds to the name of a simple attribute, setter method, or property:

(binder.bind("thing", to="module.Thing").
    attributes(id=1, set_name="First thing"))

If any value in nvpairs or keywords is a class, unbound function, or module type, it is automatically turned into a Reference:

# SomeClass becomes
# ``Reference(format_dotted_name(SomeClass))``
(binder.bind("thing", to="module.Thing").
    attributes(id=1, set_class=SomeClass))

Warning

In the above example, there must exist a component that has the ID “package.module.SomeClass”:

binder.bind(SomeClass).init(...).attributes(...)
# - or -
(binder.bind("package.module.SomeClass").
    init(...).attributes(...))

If more flexibility is needed, simply create and pass in an explicit Reference:

(binder.bind("something", to=SomeClass).
    init(...).attributes(...))
...
(binder.bind("thing", to="module.Thing").
    attributes(id=1, set_class=Reference("something")))

Changed in version 2.0.0: Successive calls to this method on the same instance have a cumulative effect.