<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>Fueled by Lemons</title>
  <link href="http://fueledbylemons.com/atom.xml" rel="self"/>
  <link href="http://fueledbylemons.com/"/>
  <updated>2014-11-06T21:04:02+00:00</updated>
  <id>http://fueledbylemons.com/</id>
  <author>
    <name>Alexey Bezhan</name>
    <email>al@fueledbylemons.com</email>
  </author>

  
  <entry>
    <title>Why UltiSnips?</title>
    <link href="http://fueledbylemons.com/blog/2011/07/27/why-ultisnips"/>
    <updated>2011-07-27T00:00:00+00:00</updated>
    <id>http://fueledbylemons.com/blog/2011/07/27/why-ultisnips</id>
    <content type="html">&lt;p&gt;When it comes to snippets, Vim has a somewhat large number of available plugins. Not necessarily
the most popular, but definitely the most mentioned seems to be snipMate. Now, snipMate is
a great plugin: it’s minimalistic, it has simple snippets syntax and it does its work really
really well. But it doesn’t do much, and judging from the lack of activity in the official
repository, it doesn’t look like it will learn to do more any time soon (although, to be fair,
there is some activity on the many forks on github).&lt;/p&gt;

&lt;p&gt;To be more specific, let’s take a look at snipMate’s own docs. “Disadvantages” section lists
some of the features snipMate lacks: &lt;code&gt;$0&lt;/code&gt;, nested placeholders, multiple-line placeholders and regex
transformations of variables. These are great examples of functions that usually don’t get in
the way, but may really come in handy when you need them.&lt;/p&gt;

&lt;p&gt;UltiSnips is a Vim plugin that, while being as easy to use as snipMate, is much more feature
rich and, along with many unique features, implements everything listed above.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#getting-ultisnips&quot;&gt;Getting UltiSnips&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#features&quot;&gt;Features&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#options&quot;&gt;Options&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#interpolation&quot;&gt;Interpolation&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#usage-examples&quot;&gt;Usage examples&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#different-beginning-of-line-snippet&quot;&gt;Different beginning-of-line snippet&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#inserting-current-date&quot;&gt;Inserting current date&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#documenting-function-arguments&quot;&gt;Documenting function arguments&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#porting-snipmate-snippets&quot;&gt;Porting snipMate snippets&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;getting-ultisnips&quot;&gt;Getting UltiSnips&lt;/h2&gt;

&lt;p&gt;You can download UltiSnips from the &lt;a href=&quot;http://www.vim.org/scripts/script.php?script_id=2715&quot;&gt;Vim website&lt;/a&gt; or the official bzr
repository on &lt;a href=&quot;https://launchpad.net/ultisnips&quot;&gt;launchpad&lt;/a&gt;. Installation is quite straightforward and is explained on the
&lt;a href=&quot;http://www.vim.org/scripts/script.php?script_id=2715&quot;&gt;script page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re using &lt;a href=&quot;http://www.vim.org/scripts/script.php?script_id=2332&quot;&gt;pathogen&lt;/a&gt; and git submodules to manage your Vim plugins, there’s the
&lt;a href=&quot;https://github.com/vim-scripts/UltiSnips&quot;&gt;vim-scripts github repository&lt;/a&gt; and
&lt;a href=&quot;https://github.com/SirVer/ultisnips&quot;&gt;official github mirror&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;UltiSnips is written mostly in python and therefore requires Vim compiled with python support. This
means that it may not work where snipMate did and is expected to be somewhat slower. In practice, I
haven’t experienced any slowdowns and UltiSnips feels as fast and snappy as any snippet plugin I’ve
used.&lt;/p&gt;

&lt;p&gt;By default, UltiSnips loads &lt;code&gt;.snippets&lt;/code&gt; files from &lt;code&gt;UltiSnips&lt;/code&gt; directories in your &lt;code&gt;runtimepath&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://bazaar.launchpad.net/~sirver/ultisnips/trunk/view/head:/doc/UltiSnips.txt&quot;&gt;UltiSnips documentation&lt;/a&gt;
covers everything the plugin may do, but I’ll try to highlight the most interesting features here.&lt;/p&gt;

&lt;p&gt;As I’ve mentioned, UltiSnips supports multi-line and nested placeholders, &lt;code&gt;$0&lt;/code&gt; (last tabstop) and
placeholder text transformations. Most of the syntax for placeholders is the same as TextMate’s.&lt;/p&gt;

&lt;p&gt;Other notable features include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ability to add filetype-independent snippets.&lt;/li&gt;
  &lt;li&gt;Support for Vim dotted filetype syntax.&lt;/li&gt;
  &lt;li&gt;Ability to “include” snippet files.&lt;/li&gt;
  &lt;li&gt;Command to show the list of available snippets.&lt;/li&gt;
  &lt;li&gt;Support for multi-word and regular expression triggers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apart from this UltiSnips has two extremely powerful tools: “options” and interpolation.&lt;/p&gt;

&lt;h3 id=&quot;options&quot;&gt;Options&lt;/h3&gt;

&lt;p&gt;Options, among other things, allow you to specify for each snippet whether it will be expanded only
at the beginning of a line, in the middle of the word, at word boundary or not. This, combined with
the option to overwrite all previously defined snippets with the same trigger, allows you to use the
same trigger to expand different snippets in different conditions.&lt;/p&gt;

&lt;h3 id=&quot;interpolation&quot;&gt;Interpolation&lt;/h3&gt;

&lt;p&gt;Interpolation allows you to execute arbitrary code and insert it’s output in your snippets.
While snipMate allowed you to call a Vim function, UltiSnips supports shell code, VimScript
and python code. There’s also a way to create standalone python functions for use inside python
code blocks.&lt;/p&gt;

&lt;h2 id=&quot;usage-examples&quot;&gt;Usage examples&lt;/h2&gt;

&lt;p&gt;A list of features sounds really nice, but sometimes it’s hard to imagine how all these things may
be useful. In this section I’ll try to demonstrate few small examples of some of the
UltiSnips features. Apart from giving you some ideas about how each feature my be used, they also
might serve as additional documentation of the snippet syntax.&lt;/p&gt;

&lt;h3 id=&quot;different-beginning-of-line-snippet&quot;&gt;Different beginning-of-line snippet&lt;/h3&gt;

&lt;p&gt;If you’ve ever used Django ORM (or any other ORM that requires you to describe model fields)
you might be familiar with the following syntax for field definition:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;name = models.CharField(max_length=255)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you want to write a snippet &lt;code&gt;char&lt;/code&gt; for inserting this line, you have two options:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Expand &lt;code&gt;char&lt;/code&gt; trigger to &lt;code&gt;${1:FIELDNAME} = models.CharField(${2:...})&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Expand &lt;code&gt;char&lt;/code&gt; trigger to &lt;code&gt;models.CharField(${1:...})&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first one works great at the beginning of line, but is cumbersome if you’ve already typed the
desired field name either by mistake or because you haven’t decided on the field type yet. The second
definition saves a bit less time and won’t allow you to define a default field name in case you need
one.&lt;/p&gt;

&lt;p&gt;Now, the good news is, as you might have guessed from the header, it is possible to define both and
make UltiSnips use the first one only on the beginning of line and the second one everywhere else.
To do this, we start with defining the shorter snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet char &quot;char field&quot;
models.CharField(${1:...})
endsnippet
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, we define the beginning-of-line snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet char &quot;char field&quot; !b
${1:FIELDNAME} = models.CharField(${2:...})
endsnippet
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, the second snippet has two characters &lt;code&gt;!b&lt;/code&gt; after the description. These are snippet
options: &lt;code&gt;b&lt;/code&gt; means that snippet should be expanded only at the beginning of a line and &lt;code&gt;!&lt;/code&gt;
overwrites all previously defined snippets with the same trigger. Without &lt;code&gt;!&lt;/code&gt; there would be
two possible expansions for &lt;code&gt;char&lt;/code&gt; at the beginning of a line so UltiSnips would show a list of
available snippets before expansion. And since beginning-of-line snippets can’t be used after you’ve
typed a field name, the short snippet remains the only available option there and will be expanded without
further questions. It is important that the beginning-of-line snippet is defined after the short
snippet, since &lt;code&gt;!&lt;/code&gt; only overwrites previously defined snippets.&lt;/p&gt;

&lt;h3 id=&quot;inserting-current-date&quot;&gt;Inserting current date&lt;/h3&gt;

&lt;p&gt;This is a simple example of interpolation. Let’s define a snippet &lt;code&gt;date&lt;/code&gt; that will insert the
current date:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet date
`!v strftime(&quot;%Y-%m-%d&quot;)`
endsnippet
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Backticks indicate interpolation and &lt;code&gt;!v&lt;/code&gt; means that we’re using vimscript. &lt;code&gt;strftime&lt;/code&gt; is the name
of Vim function that returns date and time in the specified format. If you want this snippet to be
available in files of any type, you can define it inside &lt;code&gt;all.snippets&lt;/code&gt; file (analogous to
&lt;code&gt;_.snippets&lt;/code&gt; file in snipMate) in one of the directories in snippet search path.&lt;/p&gt;

&lt;h3 id=&quot;documenting-function-arguments&quot;&gt;Documenting function arguments&lt;/h3&gt;

&lt;p&gt;There is an advanced example of interpolation usage in &lt;a href=&quot;http://bazaar.launchpad.net/~sirver/ultisnips/trunk/view/head:/UltiSnips/python.snippets&quot;&gt;python.snippets&lt;/a&gt; file bundled with
UltiSnips. &lt;code&gt;smart class&lt;/code&gt; and &lt;code&gt;smart def&lt;/code&gt; snippets use python interpolation to insert a special line
into function docstring for each function argument. I won’t explain the snippet here, but together
with the &lt;code&gt;box&lt;/code&gt; snippet from &lt;a href=&quot;http://bazaar.launchpad.net/~sirver/ultisnips/trunk/view/head:/UltiSnips/all.snippets&quot;&gt;all.snippets&lt;/a&gt; it shows how much can be achieved with python
interpolation in UltiSnips. I haven’t made much use of it yet, but it seems that such snippets may
be a viable alternative for some types of Vim plugins.&lt;/p&gt;

&lt;p&gt;More snippet examples can be found in snippets bundled with UltiSnips.&lt;/p&gt;

&lt;h2 id=&quot;porting-snipmate-snippets&quot;&gt;Porting snipMate snippets&lt;/h2&gt;

&lt;p&gt;UltiSnips comes with a nice collection of snippets, but if you’ve been using snipMate for a long
time, there’s a chance that you’ve grown accustomed to snippets that aren’t readily available in
UltiSnips. The good news is that, at the moment, pretty much every snippet can be converted from
snipMate to UltiSnips format in few easy steps.&lt;/p&gt;

&lt;p&gt;The main difference in snippet file syntax is that snipMate uses indentation to find the end of the
snippet text, while UltiSnips requires and explicit &lt;code&gt;endsnippet&lt;/code&gt; line at the end. Overcoming this
difference is easy: remove leading indentation from snippet text and add &lt;code&gt;endsnippets&lt;/code&gt; line at the
end of the snippet, converting&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet example
        example snippet text
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet example
example snippet text
endsnippet
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second common difference is the snippet description text, which should be quoted in UltiSnips:
&lt;code&gt;snippet try Try/Except&lt;/code&gt; should be converted to &lt;code&gt;snippet try &quot;Try/Except&quot;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At last, if you have snippets that call VimScript functions, you should either prepend them
with &lt;code&gt;!v&lt;/code&gt; to indicate VimScript interpolation (&lt;code&gt;strftime(&quot;%Y-%m-%d&quot;)&lt;/code&gt; should be converted to
&lt;code&gt;!v strftime(&quot;%Y-%m-%d&quot;)&lt;/code&gt;) or rewrite them using one of the other UltiSnips interpolation types.&lt;/p&gt;

&lt;p&gt;This should cover most of the differences and make the transition painless. There’s a chance that
some non-trivial snippets may require some additional changes, but the above steps were enough for
everything I’ve tried to convert.
If you’re converting snippets from &lt;code&gt;_.snippets&lt;/code&gt; file, don’t forget that you should rename it to
&lt;code&gt;all.snippets&lt;/code&gt; to make snippets available regardless of the filetype in UltiSnips.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; It looks like the latest version of UltiSnips comes with a script for automatic snippet
conversion from both snipMate and TextMate formats.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://bazaar.launchpad.net/~sirver/ultisnips/trunk/view/head:/doc/UltiSnips.txt&quot;&gt;UltiSnips documentation&lt;/a&gt; contains a lot more information about what UltiSnips can
and can’t do and you should probably consult it if you have any questions.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Server configs and fabric</title>
    <link href="http://fueledbylemons.com/blog/2011/04/09/server-configs-and-fabric"/>
    <updated>2011-04-09T00:00:00+00:00</updated>
    <id>http://fueledbylemons.com/blog/2011/04/09/server-configs-and-fabric</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;http://fabfile.org/&quot;&gt;Fabric&lt;/a&gt; is a deployment automation tool popular among python developers. Since
1.0 release it allows you to do pretty much anything you would normally do manually with ssh.&lt;/p&gt;

&lt;p&gt;Fabric is simple and concentrates on providing an API, a number of utility functions and an easy
way to run tasks defined inside fabfiles. But although fabric allows you to execute functions
on multiple hosts at once, it lacks any sufficiently advanced instruments to store and apply
server-specific options. Implementing such instrument is the main goal of this post.&lt;/p&gt;

&lt;p&gt;I’ve spent some time researching possible solutions to the following problem, however I should note
that I could’ve missed some obvious and easier solution or, since this was before the release of
fabric 1.0, such solution may have been introduced to fabric itself by the time of this writing.&lt;/p&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#the-problem&quot;&gt;The Problem&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#solution-in-a-nutshell&quot;&gt;Solution in a nutshell&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#detailed-explanation&quot;&gt;Detailed explanation&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#requirements&quot;&gt;Requirements&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#fabric-handling-of-env&quot;&gt;Fabric handling of env&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#config-file-syntax&quot;&gt;Config file syntax&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#parsing-config-files&quot;&gt;Parsing config files&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#server-selection-task&quot;&gt;Server selection task&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#task-setup-decorator&quot;&gt;Task setup decorator&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-whole-thing&quot;&gt;The whole thing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Suppose we have three projects: ProjectA, ProjectB and ProjectC deployed on three corresponding
servers: ServerA, ServerB and ServerC. We want:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;to deploy each project to its own server with one shell command&lt;/li&gt;
  &lt;li&gt;to deploy all projects at once with one shell command&lt;/li&gt;
  &lt;li&gt;to run maintenance tasks on a subgroup of servers with one shell command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fabric solves first problem really good, but it proposes no apparent solution for the rest.&lt;/p&gt;

&lt;p&gt;Let’s say our deployment workflow is similar for all projects and consists of two basic steps:
updating project source code from repository and restarting the server process. We could implement
such workflow with three fabric tasks:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Writing these tasks for a single project is simple, however since we need them for each project it
would be nice to follow the DRY principle and implement them only once for use on any project with
similar deployment workflow.&lt;/p&gt;

&lt;p&gt;And that’s where things get tricky. We can’t really implement the &lt;code&gt;update()&lt;/code&gt; function without either
hardcoding the source code repository location and shell command to pull the update or some sort of
project config with repository location, which &lt;code&gt;update()&lt;/code&gt; can address.&lt;/p&gt;

&lt;p&gt;The same applies to &lt;code&gt;restart()&lt;/code&gt; function. It’s easy to write a function that restarts &lt;code&gt;apache&lt;/code&gt;,
but what if one of our projects is served by &lt;code&gt;nginx&lt;/code&gt; or &lt;code&gt;lighttpd&lt;/code&gt;? How can we write a &lt;code&gt;restart()&lt;/code&gt;
function that takes care of all the boilerplate of process restart(switching to correct user,
performing appropriate checks and so on) and restarts the correct process depending on the project
config.&lt;/p&gt;

&lt;p&gt;When working with fabric, this is usually solved by defining environment-initializing functions
for each server:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;server_a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hosts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;a.example.org&amp;#39;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;server_b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But this makes it impossible to run one task on multiple servers at once.
I.e. running&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab server_a server_b deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;won’t do what you expect it to (if you expect it to deploy to two servers, that is).&lt;/p&gt;

&lt;p&gt;Following is a fabfile template that will allow you to use server-specific configuration options
inside your task, an instruction on how to use it and a detailed explanation of implementation.&lt;/p&gt;

&lt;h2 id=&quot;solution-in-a-nutshell&quot;&gt;Solution in a nutshell&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Download base &lt;a href=&quot;/static/code/fabfile.py&quot;&gt;fabfile.py&lt;/a&gt; template. It requires fabric version 0.9 or later,
includes a bunch of internal functions and defines one task: &lt;code&gt;s&lt;/code&gt;.
You can add your own tasks to this fabfile or, if you’re using fabric version 1.0 or later, you
can create &lt;a href=&quot;http://fabric.readthedocs.org/en/1.0.1/usage/fabfiles.html&quot;&gt;fabfile folder&lt;/a&gt;,
move it there and define your own tasks in other files inside that folder.&lt;/li&gt;
  &lt;li&gt;Add &lt;code&gt;@_setup&lt;/code&gt; decorator to tasks that need additional server options.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Address all required server options as &lt;code&gt;env&lt;/code&gt; attributes from your fabric tasks:&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;@_setup
def restart():
    if env.webserver == &quot;apache&quot;:
        # restart apache
    else:
        # ...
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Attributes aren’t namespaced and get automatically added to &lt;code&gt;env&lt;/code&gt; by &lt;code&gt;_setup&lt;/code&gt; decorator so you have
to be careful not to overwrite any of the
&lt;a href=&quot;http://docs.fabfile.org/en/1.0.1/usage/env.html&quot;&gt;builtin env arguments&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;In the root folder of your project create a file &lt;code&gt;server_config.yaml&lt;/code&gt; or &lt;code&gt;server_config.json&lt;/code&gt;
if you prefer json syntax. To use YAML config file you need to have PyYaml
installed (&lt;code&gt;pip install pyyaml&lt;/code&gt; will generally suffice), while
JSON requires either &lt;code&gt;simplejson&lt;/code&gt; or python 2.6 with built-in &lt;code&gt;json&lt;/code&gt; module.&lt;/li&gt;
  &lt;li&gt;Open created file and define server parameters for your servers. &lt;code&gt;host&lt;/code&gt;
is the only required parameter and it must contain server hostname:
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;YAML:&lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;server_a:
      host: a.example.com
      webserver: apache
      repository: git@example.com:project.git
server_b:
      host: b.example.com
      webserver: nginx
      web_folder: /var/www/project
      ...
&lt;/code&gt;&lt;/pre&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;JSON:&lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;{
  &quot;server_a&quot;:{
      &quot;host&quot;: &quot;a.example.com&quot;,
      &quot;webserver&quot;: &quot;apache&quot;,
      &quot;repository&quot;: &quot;git@example.com:project.git&quot;
  },
  &quot;server_b&quot;: {
      ...
  }
}
&lt;/code&gt;&lt;/pre&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Define server groups in the config file:
    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;YAML:&lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;frontend-servers: [server_a, server_c, ...]
&lt;/code&gt;&lt;/pre&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;JSON:&lt;/p&gt;

        &lt;pre&gt;&lt;code&gt;{
  ...
  &quot;frontend-servers&quot;: [&quot;server_a&quot;, &quot;server_c&quot;, ...]
}
&lt;/code&gt;&lt;/pre&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After everything is set up, you can call &lt;/p&gt;

&lt;pre&gt;&lt;code&gt; $ fab s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to list all available server configs, or&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; $ fab s:server_a,server_b,frontend-servers task
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to execute &lt;code&gt;task&lt;/code&gt; once for server A, server B and members of &lt;code&gt;frontend-servers&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;Since fabric looks for fabfile in parent directories you can move your file to the common parent
directory of your projects path and leave server config inside project directory. This way you
can use the same fabfile with different projects and keep server config together with other
project-related files.&lt;/p&gt;

&lt;p&gt;If the above worked as expected and you aren’t interested in any further explanations you can stop
reading now.&lt;/p&gt;

&lt;p&gt;Having described the basic usage and configuration, let’s move on to listing the initial
requirements and internal details of implementation.&lt;/p&gt;

&lt;h2 id=&quot;detailed-explanation&quot;&gt;Detailed explanation&lt;/h2&gt;

&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Proposed solution must be &lt;strong&gt;portable&lt;/strong&gt; in a sense that it should be possible to transfer the whole
thing to other developers in one step and it should work on any machine with
fabric already installed. This basically means patching fabric isn’t an option.&lt;/li&gt;
  &lt;li&gt;Conforming to the &lt;strong&gt;DRY&lt;/strong&gt; principle means the user has to describe the task and list specific
server options only once.&lt;/li&gt;
  &lt;li&gt;Allow &lt;strong&gt;any number of parameters&lt;/strong&gt; defined by user for each server.&lt;/li&gt;
  &lt;li&gt;Allow &lt;strong&gt;parameters of arbitrary type&lt;/strong&gt;: string, integer, lists and python dictionaries when
needed.&lt;/li&gt;
  &lt;li&gt;Introduce as &lt;strong&gt;little overhead&lt;/strong&gt; as possible to both writing tasks and running them. Fabric is
mostly about making the common thing easy and it should remain that way.&lt;/li&gt;
  &lt;li&gt;Make it &lt;strong&gt;easy to add&lt;/strong&gt; servers and config parameters to existing configuration files. &lt;/li&gt;
  &lt;li&gt;It should &lt;strong&gt;scale&lt;/strong&gt; to any number of servers. Defining and running env initialization tasks may
work for 2 or 3 servers, but it is basically unusable for 10 or more.&lt;/li&gt;
  &lt;li&gt;Allow defining &lt;strong&gt;server groups&lt;/strong&gt; the same way &lt;code&gt;env.roledefs&lt;/code&gt; allows to arrange hosts in groups.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Allow to run one task on multiple servers&lt;/strong&gt; using only one shell command.&lt;/li&gt;
  &lt;li&gt;Must be &lt;strong&gt;compatible&lt;/strong&gt; with fabric’s built-in methods to declare hosts and tasks that don’t need
this functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;fabric-handling-of-env&quot;&gt;Fabric handling of env&lt;/h3&gt;

&lt;p&gt;Fabric keeps config options (including list of hosts and user info) inside global environment
variable &lt;code&gt;env&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;env&lt;/code&gt; itself isn’t very interesting: it’s a simple dictionary subclass
(&lt;a href=&quot;https://github.com/bitprophet/fabric/blob/master/fabric/state.py&quot;&gt;fabric/state.py&lt;/a&gt;) that allows to
access its keys as attributes. Inside it are stored all the settings that influence underlying ssh 
and internal settings. Full list of environment variables can be found in 
&lt;a href=&quot;http://docs.fabfile.org/en/1.0.1/usage/env.html&quot;&gt;fabric documentation&lt;/a&gt;.
There are, however, two important details about env in
&lt;a href=&quot;https://github.com/bitprophet/fabric/blob/master/fabric/main.py&quot;&gt;fabric main loop&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Once the task started running you can’t in any way change its destination hosts.
Any changes you make to &lt;code&gt;env.hosts&lt;/code&gt; will influence the following tasks, not the current one.
This is due to the fact that fabric iterates over internal &lt;code&gt;hosts&lt;/code&gt; variable that
is set once for each task before the task starts running.&lt;/li&gt;
  &lt;li&gt;Fabric deletes duplicates from host list by transforming it into set and back,
so initial hosts order is lost during execution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second fact is interesting outside the context of our problem because it means that you
can’t possibly know whether certain task will be first executed on server A or server B and so
you shouldn’t rely on any specific order in your deployment routines.&lt;/p&gt;

&lt;h3 id=&quot;config-file-syntax&quot;&gt;Config file syntax&lt;/h3&gt;

&lt;p&gt;Multiple-server config file is best represented by a dictionary: each server is represented by
server name (key) and parameters (value). Since we want to support arbitrary number of parameters,
we will represent each server config also as a dictionary, where parameter names will be keys, and
parameter values will be, well, values. We’re using symbolic server names as keys, so each config
must provide server’s hostname in the standard required parameter &lt;code&gt;host&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’ve mentioned the support for server groups before. Server group is basically a list of server
names stored under group name key. The reason for this is quite simple: if we have a number of
database servers and want to backup them all for example, instead of writing&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab dbserver_1 dbserver_2 ... backup
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;each time, we would rather store the list of database servers in our config file under the
key “dbservers” and start the actual task with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab dbservers backup
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Server groups may be nested, for example:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;dbservers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dbserver_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;dbserver_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;allservers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dbservers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;webservers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To write the actual config files we could use either YAML or JSON syntax: YAML is somewhat easier to
read and write by hand, but JSON usually won’t require any additional packages since most developers
already have either &lt;code&gt;simplejson&lt;/code&gt; or python 2.6.&lt;/p&gt;

&lt;p&gt;We’ll support both and choose the appropriate loader depending on the installed libraries and
available config file extension. Examples of server configs are shown in “Solution in a nutshell”
section.&lt;/p&gt;

&lt;h3 id=&quot;parsing-config-files&quot;&gt;Parsing config files&lt;/h3&gt;

&lt;p&gt;We’ve come to the actual implementation details. First thing to do is to find the config file
and convert it from markup language to python data structure. We’ll start with figuring out what
packages are available:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;YAML_AVAILABLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;yaml&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;YAML_AVAILABLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;JSON_AVAILABLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;simplejson&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;json&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;json&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;JSON_AVAILABLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re looking for &lt;code&gt;PyYAML&lt;/code&gt; for YAML and either &lt;code&gt;simplejson&lt;/code&gt; or built-in &lt;code&gt;json&lt;/code&gt; module for JSON.
The actual function that transforms file to python dictionary:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_load_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Find and parse server config file.&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    If `config` keyword argument wasn&amp;#39;t set look for default&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;#39;server_config.yaml&amp;#39; or &amp;#39;server_config.json&amp;#39; file.&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;splitext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;config&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&amp;#39;server_config.yaml&amp;#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;server_config.yaml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; \
                         &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;server_config.json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;Error. &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; file not found.&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;YAML_AVAILABLE&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;.yaml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JSON_AVAILABLE&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;.json&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;Parser package not available&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Open file and deserialize settings.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It starts by looking for one of the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Existing file with &lt;code&gt;config&lt;/code&gt; keyword argument name.&lt;/li&gt;
  &lt;li&gt;Existing &lt;code&gt;server_config.yaml&lt;/code&gt; file.&lt;/li&gt;
  &lt;li&gt;Existing &lt;code&gt;server_config.json&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If none are found, function will print error message and return empty dictionary. If one of the files
exists and the corresponding package is available, we parse the file and return python dictionary.&lt;/p&gt;

&lt;h3 id=&quot;server-selection-task&quot;&gt;Server selection task&lt;/h3&gt;

&lt;p&gt;Now we need to actually tell fabric to load and parse config files and a way to choose which servers
we’ll be using for task execution. For this very reason we have created a single task &lt;code&gt;s&lt;/code&gt;. You call
it before your own tasks and pass it server names the way you would pass arguments to any fabric
task:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab s:server_a,server_b,server_group mytask mytask2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Additionally, you can also specify the location of config file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab s:server_a,server_b,config=servers.yaml mytask mytask2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here’s what it does:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Set destination servers or server groups by comma delimited list of names&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Load config&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_load_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# If no arguments were recieved, print a message with a list of&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# available configs.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;#39;No server name given. Available configs:&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Create `group` - a dictionary, containing copies of configs for selected&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# servers. Server hosts are used as dictionary keys, which allows us to&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# connect current command destination host with the correct config. This&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# is important, because somewhere along the way fabric messes up the hosts&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# order, so simple list index incrementation won&amp;#39;t suffice.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# For each given server name&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#  Recursive function call to retrieve all server records. If `name`&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# is a group(e.g. `all`) - get its members, iterate through them and&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# create `group` record. Else, get fields from `name` server record.&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# If requested server is not in the settings dictionary output error&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# message and list all available servers.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_build_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


    &lt;span class=&quot;c&quot;&gt;# Copy server hosts from `env.group` keys - this gives us a complete list of&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# unique hosts to operate on. No host is added twice, so we can safely add&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# overlaping groups. Each added host is guaranteed to have a config record&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# in `env.group`.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hosts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;s&lt;/code&gt; takes as arguments a list of server names and, optionally, a keyword argument &lt;code&gt;config&lt;/code&gt;,
containing the path to server config file. It starts with calling &lt;code&gt;_load_config&lt;/code&gt; to parse config
file. If no server names were specified, &lt;code&gt;s&lt;/code&gt; will print all server names found by &lt;code&gt;_load_config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We create a &lt;code&gt;group&lt;/code&gt; dictionary in &lt;code&gt;env&lt;/code&gt; and for each server name user has specified we call
&lt;code&gt;_build_group&lt;/code&gt; function, which as we’ll see soon, modifies &lt;code&gt;env.group&lt;/code&gt; to store server configs. The
last thing we do in &lt;code&gt;s&lt;/code&gt; is rewriting &lt;code&gt;env.hosts&lt;/code&gt; with &lt;code&gt;env.group.keys()&lt;/code&gt;, which will influence the
next task. At this point &lt;code&gt;env.hosts&lt;/code&gt; will contain a list of server hosts that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Belong to the servers that user has specified.&lt;/li&gt;
  &lt;li&gt;Belong to the servers that appear inside server groups that user has specified.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s look at &lt;code&gt;_build_group&lt;/code&gt; function next to see how this list gets computed:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_build_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Recursively walk through servers dictionary and search for all server records.&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# We&amp;#39;re going to reference server a lot, so we&amp;#39;d better store it.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# If `name` exists in servers dictionary we&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# check whether it&amp;#39;s a group by looking for `members`&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fabric&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;debug&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is a group, getting members&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# and call this function for each of them.&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;_build_group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# When, finally, we dig through to the standalone server records, we&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# retrieve configs and store them in `env.group`&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fabric&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;debug&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is a server, filling up env.group&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;host&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;Error. &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; config not found. &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;                    Run `fab s` to list all available configs&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Function starts by checking &lt;code&gt;servers&lt;/code&gt; dictionary (which is just a parsed version of config file) for
existing &lt;code&gt;name&lt;/code&gt; key, which indicates whether user specified a valid server name or not. If the key
was found, we check whether its value is a list, in which case we call &lt;code&gt;_build_group&lt;/code&gt; for each item
from that list, or a dictionary, in which case we add server config to &lt;code&gt;env.group&lt;/code&gt; dictionary using
&lt;code&gt;server[&quot;host&quot;]&lt;/code&gt; as key.&lt;/p&gt;

&lt;p&gt;Thus, for each server name that is either directly specified by user or is a member of a server
group, specified by user, we store config dictionary inside &lt;code&gt;env.group[server[&#39;host&#39;]]&lt;/code&gt;. This is
exactly the reason why &lt;code&gt;host&lt;/code&gt; is required in server configs - since we need a way to create a host
list for fabric and a way to connect server on which we are executing tasks at the moment with
its config dictionary.&lt;/p&gt;

&lt;p&gt;We could stop at this point, since we have everything we need for a working solution to the
specified problem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ fab s:server_a,server_b mytask
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will allow us to get current server options from &lt;code&gt;mytask&lt;/code&gt;, e.g. to get &lt;code&gt;repository&lt;/code&gt; option value for
current server we could do:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;repository&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;but this looks somewhat ugly, and doesn’t really qualify as “making common thing easy”.
There’s a number of things we could do to simplify this, one of which is to define a helper function:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, &lt;code&gt;_get(&quot;repository&quot;)&lt;/code&gt; will return either the option value or &lt;code&gt;None&lt;/code&gt; if option isn’t defined
for the current server. This is much nicer to use, but one problem still remaining with this
solution is that it isn’t compatible with the usual way to store server settings directly as attributes
of &lt;code&gt;env&lt;/code&gt; (which is common inside special initialization tasks). This means that one task won’t work out of the box
with both types of server initialization.&lt;/p&gt;

&lt;h3 id=&quot;task-setup-decorator&quot;&gt;Task setup decorator&lt;/h3&gt;

&lt;p&gt;To resolve the issue we’ll need to rewrite all keys of current server configuration from
&lt;code&gt;env.group[env.host_string][key]&lt;/code&gt; to &lt;code&gt;env.key&lt;/code&gt; before each task run. Let’s define a &lt;code&gt;_setup&lt;/code&gt;
decorator, that will do just that:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Copies server config settings from `env.group` dictionary to env variable.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    This way, tasks have easier access to server-specific variables:&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;        `env.owner` instead of `env.group[env.host][&amp;#39;owner&amp;#39;]`&lt;/span&gt;

&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;task_with_setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# If `s:server` was run before the current command - then we should copy&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# values to `env`. Otherwise, hosts were passed through command line&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# with `fab -H host1,host2 command` and we skip.&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;setattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fabric&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;#39;debug&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;puts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;[env] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; : &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task_with_setup&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now all you have to do to make &lt;code&gt;env.key&lt;/code&gt; access available inside your task is to decorate it with
&lt;code&gt;@_setup&lt;/code&gt;. Generally this works just fine, however you should be wary of two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If you define a config option with the same name as one of the built-in &lt;code&gt;env&lt;/code&gt; attributes, like
&lt;code&gt;host_string&lt;/code&gt; or &lt;code&gt;hosts&lt;/code&gt;, things might break in unexpected ways.&lt;/li&gt;
  &lt;li&gt;Attributes aren’t deleted after the task has finished. This means that if current server config
doesn’t define &lt;code&gt;key1&lt;/code&gt; option, &lt;code&gt;env.key1&lt;/code&gt; might still exist if one of previous server configs
defined it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-whole-thing&quot;&gt;The whole thing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/static/code/fabfile.py&quot;&gt;Complete source code&lt;/a&gt; contains everything described here, except the
&lt;code&gt;_get()&lt;/code&gt; function example. Note that link above points to the most recent published version of the
script, while code snippets in this post may be outdated.&lt;/p&gt;
</content>
  </entry>
  
 
</feed>
