<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kermit Alexander II</title>
    <description>The latest articles on DEV Community by Kermit Alexander II (@dangerontheranger).</description>
    <link>https://dev.to/dangerontheranger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F114589%2Fb7ac513a-1f8e-4b69-8e18-449ac4b6ca85.jpg</url>
      <title>DEV Community: Kermit Alexander II</title>
      <link>https://dev.to/dangerontheranger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dangerontheranger"/>
    <language>en</language>
    <item>
      <title>Dependency Injection with Import Hooks in Python 3</title>
      <dc:creator>Kermit Alexander II</dc:creator>
      <pubDate>Wed, 26 Dec 2018 14:18:53 +0000</pubDate>
      <link>https://dev.to/dangerontheranger/dependency-injection-with-import-hooks-in-python-3-5hap</link>
      <guid>https://dev.to/dangerontheranger/dependency-injection-with-import-hooks-in-python-3-5hap</guid>
      <description>&lt;p&gt;&lt;code&gt;sys.meta_path&lt;/code&gt; is one of the best-kept secrets in the Python standard library, despite being squarely located in a module every Python programmer is familiar with. Some might say the secrecy is well-warranted, though; it's not usually a good idea to go messing around with tools and functions that may subvert programmer expectations the way this oddly-named list potentially can. But isn't "do not" better than "know not?" So before a quick primer on dependency injection, let's examine some of the ins and outs of &lt;code&gt;sys.meta_path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Whenever you use an &lt;code&gt;import&lt;/code&gt; statement, say, &lt;code&gt;import os&lt;/code&gt;, that's functionally equivalent to the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;__import__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"os"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's a mild oversimplification, since stuff like submodules would require a little bit more work (as we'll have to deal with later, actually), but the main point to be made here is that the &lt;code&gt;__import__&lt;/code&gt; built-in does the heavy lifting under the hood for module imports in Python, and now we know that it exists, we can examine its control flow. First, it checks &lt;code&gt;sys.modules&lt;/code&gt;, a dict containing already-loaded modules, just in case we've already imported the requested module before. Next, it checks &lt;code&gt;sys.path&lt;/code&gt; - a list containing filesystem paths for Python to search through for potential modules; you probably know about this list already. However, if the &lt;code&gt;sys.meta_path&lt;/code&gt; list is not empty, than &lt;em&gt;before&lt;/em&gt; looking through &lt;code&gt;sys.path&lt;/code&gt;, every meta importer hook in &lt;code&gt;sys.meta_path&lt;/code&gt; will be queried before the filesystem is checked, giving us an easy way to intercept module imports and do all sorts of freakishly wonderful things, like dependency injection.&lt;/p&gt;

&lt;p&gt;So what is dependency injection? It's roughly an applied version of the &lt;a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle"&gt;dependency inversion principle&lt;/a&gt;, often for module-sized stuff. Say your application has several different front-ends, maybe targeting different platforms or UI toolkits. As long as you have a common interface, dependency injection would allow you to do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info_getter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_user_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dependency_injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"frontend"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display_user_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;"So what? I can do that already with a Facade or something, maybe a config switch," I can hear you say. For very simple use cases, that may actually be better - simple is better than complex, remember? However, with dependency injection, we can easily provide module-level reuse, as well as significantly cut down on boilerplate. What if I told you, by the end of this article, you could write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;myapp.dependency.frontend&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt;
&lt;span class="n"&gt;popup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;popup_style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;popup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;One import that works regardless of whether we're using GTK+, Qt, ncurses, or whatever your program needs to function. You might be thinking some very dark thoughts at this point. It's true, all sorts of dark magic could be performed depending on the details of our import hooks. But readability counts, so it's important that we not stray too far off the beaten path. So let's set some requirements for our dependency injection framework before writing our implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All dependency-injected import paths begin with &lt;code&gt;myapp.virtual&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provided dependencies are known beforehand and registered with a &lt;code&gt;provide&lt;/code&gt; method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the magic - and there's not all that much - relies almost entirely on creating a meta path finder and adding it to &lt;code&gt;sys.meta_path&lt;/code&gt;. A meta path finder is a very simple object with a single public method, &lt;code&gt;find_spec&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;importlib.abc&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;importlib.machinery&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DependencyInjectorFinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MetaPathFinder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# we'll write the loader in a minute, hang tight
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="s"&gt;"""Attempt to locate the requested module
        fullname is the fully-qualified name of the module,
        path is set to __path__ for sub-modules/packages, or None otherwise.
        target can be a module object, but is unused in this example.
        """&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provides&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gen_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_gen_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;machinery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModuleSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;
&lt;span class="c1"&gt;# we'll also add it to sys.meta_path later
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If a meta path finder provides the requested module, then it should return an instance of the &lt;code&gt;importlib.machinery.ModuleSpec&lt;/code&gt; class, which is a fairly simple affair with a small handful of attributes that lets Python's import machinery know what it needs to know to take the next steps in importing the module the user requested. For our purposes, we're interested in two attributes (the only required ones): &lt;code&gt;ModuleSpec.name&lt;/code&gt;, which is the name of the requested module, and &lt;code&gt;ModuleSpec.loader&lt;/code&gt;, which is the loader object that Python should use to actually load the module - you'll notice the &lt;code&gt;self._loader&lt;/code&gt; lines above that reference a loader object, as well. A loader object is a very simple class with two required methods in modern Python (3.4 onwards): &lt;code&gt;create_module&lt;/code&gt;, which takes a &lt;code&gt;ModuleSpec&lt;/code&gt; as its sole argument and returns an object that Python will consider to be the new module, and &lt;code&gt;exec_module&lt;/code&gt;, which takes the new module as its sole argument and executes it. So a no-op, barebones loader looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Loader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Loader&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;NotImplementedError&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exec_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nb"&gt;NotImplementedError&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In addition to those two methods, we should implement the &lt;code&gt;provide&lt;/code&gt; method we talked about earlier to signal that a certain dependency is provided, in addition to the &lt;code&gt;provides&lt;/code&gt; method that our finder referenced earlier, that indicates whether the requested module is part of our dependency injection framework. Here's the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;importlib.abc&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;types&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DependencyInjectorLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;importlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Loader&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_COMMON_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myapp.virtual."&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="c1"&gt;# create a dummy module to return when Python attempts to import
&lt;/span&gt;        &lt;span class="c1"&gt;# myapp and myapp.virtual, the :-1 removes the last "." for
&lt;/span&gt;        &lt;span class="c1"&gt;# aesthetic reasons :) 
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_dummy_module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModuleType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_COMMON_PREFIX&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="c1"&gt;# set __path__ so Python believes our dummy module is a package
&lt;/span&gt;        &lt;span class="c1"&gt;# this is important, since otherwise Python will believe our
&lt;/span&gt;        &lt;span class="c1"&gt;# dummy module can have no submodules
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_dummy_module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__path__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="s"&gt;"""Register a service as provided via the given module
        A service is any Python object in this context - an imported module,
        a class, etc."""&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;provides&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_truncate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# this checks if we should return the dummy module,
&lt;/span&gt;            &lt;span class="c1"&gt;# since this evaluates to True when importing myapp and
&lt;/span&gt;            &lt;span class="c1"&gt;# myapp.virtual
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_COMMON_PREFIX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="s"&gt;"""Create the given module from the supplied module spec
        Under the hood, this module returns a service or a dummy module,
        depending on whether Python is still importing one of the names listed
        in _COMMON_PREFIX.
        """&lt;/span&gt;
        &lt;span class="n"&gt;service_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_truncate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# return our dummy module since at this point we're loading
&lt;/span&gt;            &lt;span class="c1"&gt;# *something* along the lines of "myapp.virtual" that's not
&lt;/span&gt;            &lt;span class="c1"&gt;# a service
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_dummy_module&lt;/span&gt;
        &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_services&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exec_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="s"&gt;"""Execute the given module in its own namespace
        This method is required to be present by importlib.abc.Loader,
        but since we know our module object is already fully-formed,
        this method merely no-ops.
        """&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_truncate_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="s"&gt;"""Strip off _COMMON_PREFIX from the given module name
        Convenience method when checking if a service is provided.
        """&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fullname&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_COMMON_PREFIX&lt;/span&gt;&lt;span class="p"&gt;):]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There's seemingly a lot of code here at first glance (even though most of it is comments!), so let's walk through this class' methods. Up first are the &lt;code&gt;provide&lt;/code&gt; and &lt;code&gt;provides&lt;/code&gt; methods that we've talked about earlier; there's not a whole lot of magic present in either one. &lt;code&gt;create_module&lt;/code&gt; returns a so-called dummy module if we're trying to import either &lt;code&gt;myapp&lt;/code&gt; or &lt;code&gt;myapp.virtual&lt;/code&gt;, and there's a good reason for that. Say we have the following line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;myapp.virtual.frontend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Under the hood, this generates three distinct searches in Python's import machinery: One for &lt;code&gt;myapp&lt;/code&gt;, another for &lt;code&gt;myapp.virtual&lt;/code&gt;, and lastly one for &lt;code&gt;myapp.virtual.frontend&lt;/code&gt;. Since obviously &lt;code&gt;myapp&lt;/code&gt; and &lt;code&gt;myapp.virtual&lt;/code&gt; don't actually exist anywhere on the system, but Python will complain if they aren't loaded, we claim we provide both - note how &lt;code&gt;provides&lt;/code&gt; will return &lt;code&gt;True&lt;/code&gt; if queried for both &lt;code&gt;myapp&lt;/code&gt; and &lt;code&gt;myapp.virtual&lt;/code&gt; and return an empty dummy module to placate Python's import machinery. Only when we encounter &lt;code&gt;myapp.virtual.frontend&lt;/code&gt; do we check against the list of provided dependency-injection services.&lt;/p&gt;

&lt;p&gt;At this point, we've actually written all the plumbing we need to, so let's wrap up and see how we'd use these classes in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DependencyInjector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;"""
    Convenience wrapper for DependencyInjectorLoader and DependencyInjectorFinder.
    """&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DependencyInjectorLoader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_finder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DependencyInjectorFinder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_finder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FrontendModule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Popup:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DependencyInjector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# we could install() and then provide() if we wanted, order isn't
&lt;/span&gt;    &lt;span class="c1"&gt;# important for the below two calls
&lt;/span&gt;    &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"frontend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FrontendModule&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# note that these last three lines could exist in any other source file,
&lt;/span&gt;    &lt;span class="c1"&gt;# as long as injector.install() was called somewhere first
&lt;/span&gt;    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;myapp.virtual.frontend&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt;
    &lt;span class="n"&gt;popup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;popup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;At this point, it'd be worth it to take a step back and examine at a high level what is going on behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The call to &lt;code&gt;injector.install&lt;/code&gt; appends our &lt;code&gt;DependencyInjectorFinder&lt;/code&gt; onto the &lt;code&gt;sys.meta_path&lt;/code&gt; list, so Python will use it for future imports.&lt;/li&gt;
&lt;li&gt;The line &lt;code&gt;import myapp.virtual.frontend as frontend&lt;/code&gt; triggers three module searches, as mentioned earlier - one for &lt;code&gt;myapp&lt;/code&gt;, then &lt;code&gt;myapp.virtual&lt;/code&gt;, then &lt;code&gt;myapp.virtual.frontend&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;Python combs through &lt;code&gt;sys.meta_path&lt;/code&gt;, looking for meta path finders. If your system Python is anything like mine, our &lt;code&gt;DependencyInjectorFinder&lt;/code&gt; will be the only one not defined by &lt;code&gt;_frozen_importlib&lt;/code&gt;, which is a part of Python itself.&lt;/li&gt;
&lt;li&gt;For each found meta path finder, Python queries the finder's &lt;code&gt;find_spec&lt;/code&gt; method, seeing if the finder provides the given module.&lt;/li&gt;
&lt;li&gt;Obviously &lt;code&gt;myapp.virtual.frontend&lt;/code&gt; doesn't exist on the filesystem, so it falls to our meta path finder to handle it. In all three cases, we return a &lt;code&gt;ModuleSpec&lt;/code&gt; instance with &lt;code&gt;name&lt;/code&gt; set to the same name that Python asked if we could find, and &lt;code&gt;loader&lt;/code&gt; set to our custom &lt;code&gt;DependencyInjectorLoader&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Next, Python will call the &lt;code&gt;create_module&lt;/code&gt; method on our loader for the &lt;code&gt;ModuleSpec&lt;/code&gt; in question. For &lt;code&gt;myapp&lt;/code&gt; and &lt;code&gt;myapp.virtual&lt;/code&gt;, our loader recognizes that these are dummy modules and returns the same dummy module for both (tricking Python into believing the module was loaded), but returns the instance of &lt;code&gt;FrontendModule&lt;/code&gt; we gave it with &lt;code&gt;injector.provide()&lt;/code&gt; upon being asked to load &lt;code&gt;myapp.virtual.frontend&lt;/code&gt;. Python allows any valid object to function as a module, so a plain old class is perfectly fine to return.&lt;/li&gt;
&lt;li&gt;Python will finally call the &lt;code&gt;exec_module&lt;/code&gt; method on our loader, passing it the object we returned from &lt;code&gt;create_module&lt;/code&gt;. Python requires the &lt;code&gt;exec_module&lt;/code&gt; method to be present but doesn't really care about its behavior; normally, the method would execute the module's code in a new namespace, but since we already have fully-formed modules ready to go - either our pre-made dummy module or our &lt;code&gt;FrontendModule&lt;/code&gt; instance - we simply do nothing inside &lt;code&gt;exec_module&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wash, rinse, and repeat for each successive module search.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;After all that, &lt;code&gt;frontend.Popup&lt;/code&gt; is functionally the same thing as &lt;code&gt;FrontendModule.Popup&lt;/code&gt;, and the rest, as they say, is history.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As I mentioned earlier in the article, it's important not to abuse language features like this - with great power comes great responsibility, after all - and this especially rings true when it comes to metaprogramming. Still, there's a time and place for everything, import hooks included; but please use them responsibly.&lt;/p&gt;

&lt;p&gt;Finally, the complete code example is up as a &lt;a href="https://gist.github.com/DangerOnTheRanger/027fc0af3bd010e8265e552c5b3ab4f1"&gt;gist on GitHub&lt;/a&gt; if you'd like something you can download and pore over. Happy hooking!&lt;/p&gt;

</description>
      <category>python</category>
      <category>metaprogramming</category>
      <category>dependencyinjection</category>
    </item>
  </channel>
</rss>
