<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Stefan Scherfke - python</title><link href="https://stefan.sofa-rockers.org/" rel="alternate"></link><link href="https://stefan.sofa-rockers.org/feeds/python" rel="self"></link><id>https://stefan.sofa-rockers.org/</id><updated>2024-11-14T07:18:00+01:00</updated><entry><title>Publishing to PyPI with a Trusted Publisher from GitLab CI/CD</title><link href="https://stefan.sofa-rockers.org/2024/11/14/gitlab-trusted-publisher/" rel="alternate"></link><published>2024-11-14T07:18:00+01:00</published><updated>2024-11-14T07:18:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2024-11-14:/2024/11/14/gitlab-trusted-publisher/</id><summary type="html">&lt;p&gt;Learn how to securely upload Python packages to PyPI from GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;
pipelines using a “Trusted Publisher” (and without &lt;span class="caps"&gt;API&lt;/span&gt; tokens).
Continuously test the release process with TestPyPI on every push.
Use GitLab (deploy) environments as an additional security&amp;nbsp;measure.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;PyPA’s &lt;a href="https://docs.pypi.org/trusted-publishers/"&gt;Trusted Publishers&lt;/a&gt; let you upload Python packages directly from your &lt;span class="caps"&gt;CI&lt;/span&gt; pipeline to PyPI.
And you don’t need any long-lived secrets like &lt;span class="caps"&gt;API&lt;/span&gt; tokens.
This makes uploading Python packages not only easier than ever and more secure,&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;In this article, we’ll look at what Trusted Publishers are and how they’re more secure than using &lt;span class="caps"&gt;API&lt;/span&gt; tokens or a username/password combo.
We’ll also learn how to set up our GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline&amp;nbsp;to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;continuously test the release processes with the TestPyPI on every push to &lt;code&gt;main&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;automatically perform PyPI releases on every Git tag,&amp;nbsp;and&lt;/li&gt;
&lt;li&gt;additionally secure the process with &lt;a href="https://docs.gitlab.com/ee/ci/environments/"&gt;GitLab (deployment) environments&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href="https://docs.pypi.org/trusted-publishers/adding-a-publisher/#gitlab-cicd"&gt;official documentation&lt;/a&gt; explains most of this,
but it doesn’t go into much depth regarding GitLab pipelines and leaves a few details&amp;nbsp;unexplained.&lt;/p&gt;
&lt;section id="why-should-i-want-to-use-this"&gt;
&lt;h2&gt;Why should I want to use&amp;nbsp;this?&lt;/h2&gt;
&lt;p&gt;&lt;span class="caps"&gt;API&lt;/span&gt; tokens aren’t inherently insecure, but they do have a few&amp;nbsp;drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If they are passed as environment variables, there’s a chance they’ll leak (think of a debug &lt;code&gt;env | sort&lt;/code&gt; command in your&amp;nbsp;pipeline).&lt;/li&gt;
&lt;li&gt;If you don’t watch out, bad co-maintainers can steal the token and do mischief with&amp;nbsp;it.&lt;/li&gt;
&lt;li&gt;You have to manually renew the token from time to time, which can be annoying in the long&amp;nbsp;run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trusted Publishers can avoid these problems or, at the very least, reduce their&amp;nbsp;risk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You don’t have to manually renew any long-lived&amp;nbsp;tokens.&lt;/li&gt;
&lt;li&gt;All tokens are short-lived.
Even if they leak, they can’t be misused for&amp;nbsp;long.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we’ve learned how Trusted Publishers and protected GitLab environments work,
we will take another look at security&amp;nbsp;considerations.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-do-trusted-publishers-work"&gt;
&lt;h2&gt;How do Trusted Publishers&amp;nbsp;work?&lt;/h2&gt;
&lt;p&gt;The basic idea of Trusted Publishers is quite&amp;nbsp;simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In PyPI’s project settings, you add a Trusted Publisher and
configure it with the GitLab &lt;span class="caps"&gt;URL&lt;/span&gt; of your&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;PyPI will then only accept package uploads
if the uploader can prove that the upload comes from a &lt;span class="caps"&gt;CI&lt;/span&gt; pipeline of that&amp;nbsp;project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The technical process behind this is based on the &lt;a href="https://openid.net/connect/"&gt;OpenID Connect (&lt;span class="caps"&gt;OIDC&lt;/span&gt;)&lt;/a&gt;&amp;nbsp;standard.&lt;/p&gt;
&lt;p&gt;Essentially, the process works like&amp;nbsp;this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In your &lt;span class="caps"&gt;CI&lt;/span&gt; pipeline, you request an &lt;span class="caps"&gt;ID&lt;/span&gt; token for&amp;nbsp;PyPI.&lt;/li&gt;
&lt;li&gt;GitLab injects the short-lived token into your pipeline as a (masked) environment variable.
It is cryptographically signed by GitLab and
contains, among other things, your project’s &lt;em&gt;path with namespace&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;You use this token to authenticate with PyPI and request another token for the actual package&amp;nbsp;upload.&lt;/li&gt;
&lt;li&gt;This &lt;span class="caps"&gt;API&lt;/span&gt; token can now be used just like “normal” project-scoped &lt;span class="caps"&gt;API&lt;/span&gt;&amp;nbsp;tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href="https://docs.pypi.org/trusted-publishers/"&gt;Trusted Publishers documentation&lt;/a&gt; explains this in more&amp;nbsp;detail.&lt;/p&gt;
&lt;p&gt;One problem remains, though:
An &lt;span class="caps"&gt;ID&lt;/span&gt; token can be requested in any pipeline job and in any branch.
Malicious contributors could sneak in a pipeline job and make a corrupted&amp;nbsp;release.&lt;/p&gt;
&lt;p&gt;This is where &lt;em&gt;environments&lt;/em&gt; come&amp;nbsp;in.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="environments"&gt;
&lt;h2&gt;Environments&lt;/h2&gt;
&lt;p&gt;GitLab &lt;em&gt;environments&lt;/em&gt; represent your deployed code in your infrastructure.
Think of your code running in a container in your production or testing Kubernetes cluster;
or your Python package living on PyPI.&amp;nbsp;:-)&lt;/p&gt;
&lt;p&gt;The most important feature of environments in this context is access control:
You can protect environments, restricting deployments to them.
For protected environments, you can define users or roles that are allowed to perform deployments
and that must approve deployments.
For example, you could restrict deployments (uploads to PyPI) to all maintainers of your project,
but only after you yourself have approved each&amp;nbsp;release.&lt;/p&gt;
&lt;aside class="admonition note" id="protected-envs-note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Protected environments are a premium&amp;nbsp;feature.&lt;/p&gt;
&lt;p&gt;Non-profit open source projects/organizations &lt;a href="https://about.gitlab.com/solutions/open-source/join/"&gt;can apply&lt;/a&gt; for a free ultimate&amp;nbsp;subscription.&lt;/p&gt;
&lt;p&gt;It seems that very old projects also have this feature enabled.
Otherwise I can’t explain why I have it for &lt;a href="https://gitlab.com/sscherfke/typed-settings"&gt;Typed Settings&lt;/a&gt; but not for my other&amp;nbsp;projects…&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;To use an environment in your &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline,
you need to add it to a &lt;em&gt;job&lt;/em&gt; in the &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If we also store the name of the environment in the PyPI deployment settings,
only uploads from that environment will be allowed,
i.e. only uploads that have been authorized by selected&amp;nbsp;people.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/protected-environment_dark.png"&gt;&lt;img alt="Screenshot of GitLab CI/CD settings for protected environments." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/protected-environment_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Only maintainers can deploy to the &lt;em&gt;release&lt;/em&gt; environment
and only after Stefan approved it.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/protected-environment_light.png"&gt;&lt;img alt="Screenshot of GitLab CI/CD settings for protected environments." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/protected-environment_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Only maintainers can deploy to the &lt;em&gt;release&lt;/em&gt; environment
and only after Stefan approved it.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="security-considerations"&gt;
&lt;h2&gt;Security&amp;nbsp;Considerations&lt;/h2&gt;
&lt;p&gt;The last two sections have already hinted at this:
GitLab environments are only truly secure if you can protect&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;Let’s take a step back and consider what threats we’re trying to protect against,
so that we’ll then be able to choose the right&amp;nbsp;approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Random people doing a merge request for your&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;Contributors with the &lt;strong&gt;developer&lt;/strong&gt; role committing directly into your&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;Co-&lt;strong&gt;maintainers&lt;/strong&gt; with more permissions then a &lt;strong&gt;developer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://boehs.org/node/everything-i-know-about-the-xz-backdoor"&gt;Jia Tan&lt;/a&gt; which you trust even more than the other&amp;nbsp;maintainers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What can we do about&amp;nbsp;it?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Code in other people’s forks doesn’t have access to your project’s &lt;span class="caps"&gt;CI&lt;/span&gt; variables
nor can it request &lt;span class="caps"&gt;OIDC&lt;/span&gt; &lt;span class="caps"&gt;ID&lt;/span&gt; tokens in your project’s name.
But you need to &lt;em&gt;carefully review&lt;/em&gt; each &lt;span class="caps"&gt;MR&lt;/span&gt;!&lt;/li&gt;
&lt;li&gt;Contributors with only &lt;strong&gt;developer&lt;/strong&gt; permissions can still request &lt;span class="caps"&gt;ID&lt;/span&gt; tokens.
If you cannot use protected environments,
using an &lt;span class="caps"&gt;API&lt;/span&gt; token stored in a protected &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; variable is a more secure approach.
You should also protect your &lt;code&gt;main&lt;/code&gt; branch and all tags (using the &lt;code&gt;*&lt;/code&gt; pattern),
so that &lt;strong&gt;devleopers&lt;/strong&gt; only have access to feature branches.
You’ll find it under &lt;em class="guilabel"&gt;Settings → Repository → Protected branches/tags&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Protected &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; variables do not protect you from malicious &lt;strong&gt;maintainers&lt;/strong&gt;, though.
Even if you only allow yourself to create tags,
other maintainers still have access to protected variables.
Protected environments with only a selected set of approvers is the most secure&amp;nbsp;approach.&lt;/li&gt;
&lt;li&gt;If a very trusted co-maintainer becomes malicious,
there’s very little you can do.
Carefully review all commits and read the audit logs (&lt;em class="guilabel"&gt;Secure → Audit Events&lt;/em&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So that means for&amp;nbsp;you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you are the only maintainer of a small open source project,
just use a Trusted Publisher with (unprotected)&amp;nbsp;environments.&lt;/li&gt;
&lt;li&gt;If you belong to a larger project with multiple maintainers,
consider applying for &lt;a href="https://about.gitlab.com/solutions/open-source/join/"&gt;GitLab for Open Source&lt;/a&gt;
and use a Trusted Publisher with a protected&amp;nbsp;environment.&lt;/li&gt;
&lt;li&gt;If there are multiple contributors and you don’t have access to protected environments,
use an &lt;span class="caps"&gt;API&lt;/span&gt; token stored in a protected &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; variable
and try only grant &lt;strong&gt;developer&lt;/strong&gt; permissions to&amp;nbsp;contributors.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="admonition seealso"&gt;
&lt;p class="admonition-title"&gt;See&amp;nbsp;also&lt;/p&gt;
&lt;p&gt;Please also read about the &lt;a href="https://docs.pypi.org/trusted-publishers/security-model/#gitlab-cicd"&gt;security model and considerations&lt;/a&gt; in the PyPa&amp;nbsp;docs.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="putting-it-all-together"&gt;
&lt;h2&gt;Putting it all&amp;nbsp;together&lt;/h2&gt;
&lt;p&gt;Configuring your GitLab project to use a trusted publisher involves three main&amp;nbsp;steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update your project’s publishing settings on PyPI and&amp;nbsp;TestPyPI.&lt;/li&gt;
&lt;li&gt;Update the &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; settings for your GitLab&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;Update your project’s &lt;code class="file"&gt;pyproject.toml&lt;/code&gt; and &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="pypi-settings"&gt;
&lt;h3&gt;PyPI&amp;nbsp;Settings&lt;/h3&gt;
&lt;p&gt;Tell PyPI to trust your GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;&amp;nbsp;pipelines.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; and go to your account’s &lt;a href="https://pypi.org/manage/account/publishing/"&gt;Publishing settings&lt;/a&gt;.
Here, you can manage and add trusted publishers for your&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a new trusted publisher for &lt;em class="guilabel"&gt;GitLab&lt;/em&gt; as shown in the screenshot&amp;nbsp;below.&lt;/p&gt;
&lt;p&gt;Enter your project’s namespace (your GitLab username or the name of your organization),
the project name,
the filename of your &lt;span class="caps"&gt;CI&lt;/span&gt; def (usually &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;release&lt;/code&gt; as the environment&amp;nbsp;name!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Repeat the same steps for the &lt;a href="https://test.pypi.org/"&gt;TestPyPI&lt;/a&gt;,
but use &lt;code&gt;release-test&lt;/code&gt; as environment&amp;nbsp;name.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/pypi_dark.png"&gt;&lt;img alt="Screenshot of the PyPI trusted publisher settings." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/pypi_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Add a trusted publisher on PyPI.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/pypi_light.png"&gt;&lt;img alt="Screenshot of the PyPI trusted publisher settings." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/pypi_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Add a trusted publisher on PyPI.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="gitlab-ci-cd-settings"&gt;
&lt;h3&gt;GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;&amp;nbsp;Settings&lt;/h3&gt;
&lt;p&gt;You need to create two environments and protect the one for production&amp;nbsp;releases.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open your project in GitLab, then go to &lt;em class="guilabel"&gt;Operate → Environments&lt;/em&gt; and
click &lt;em class="guilabel"&gt;Create an environment&lt;/em&gt; to create the production&amp;nbsp;environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title:&lt;/strong&gt; &lt;code&gt;release&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;PyPI releases&lt;/code&gt; (or whatever you&amp;nbsp;want)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External &lt;span class="caps"&gt;URL&lt;/span&gt;:&lt;/strong&gt; &lt;code&gt;https://pypi.org/project/{your-project}/&lt;/code&gt;
(the &lt;span class="caps"&gt;URL&lt;/span&gt; is displayed in a few places in GitLab and helps you to quickly navigate to your project on&amp;nbsp;PyPI.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click &lt;em class="guilabel"&gt;Save&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-create_dark.png"&gt;&lt;img alt="Screenshot of the GitLab form for creating environments." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-create_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Add an environment for your deployments in Gitlab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-create_light.png"&gt;&lt;img alt="Screenshot of the GitLab form for creating environments." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-create_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Add an environment for your deployments in Gitlab.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;p&gt;Click &lt;em class="guilabel"&gt;New environment&lt;/em&gt; (in the top right corner) to create the test&amp;nbsp;environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title:&lt;/strong&gt; &lt;code&gt;release-test&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;TestPyPI releases&lt;/code&gt; (or whatever you&amp;nbsp;want)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External &lt;span class="caps"&gt;URL&lt;/span&gt;:&lt;/strong&gt; &lt;code&gt;https://test.pypi.org/project/{your-project}/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click &lt;em class="guilabel"&gt;Save&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If protected environments are available (see the &lt;a href="#protected-envs-note"&gt;note above&lt;/a&gt;),
navigate to &lt;em class="guilabel"&gt;Settings →  &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;&lt;/em&gt; and open the &lt;em&gt;Protected environments&lt;/em&gt; section.
Click the &lt;em class="guilabel"&gt;Protect an environment&lt;/em&gt;&amp;nbsp;button.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Select environment:&lt;/strong&gt; &lt;code&gt;release&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Allowed to deploy:&lt;/strong&gt; Choose a role or user, e.g. &lt;code&gt;Maintainers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approvers:&lt;/strong&gt; Choose a role or user, e.g.&amp;nbsp;yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-protect_dark.png"&gt;&lt;img alt="Screenshot of the GitLab settings for protected environments." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-protect_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Restrict who can deploy into the &lt;code&gt;release&lt;/code&gt; environment (and thus, upload to PyPI).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-protect_light.png"&gt;&lt;img alt="Screenshot of the GitLab settings for protected environments." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/environment-protect_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Restrict who can deploy into the &lt;code&gt;release&lt;/code&gt; environment (and thus, upload to PyPI).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="changes-in-project-files"&gt;
&lt;h3&gt;Changes in Project&amp;nbsp;Files&lt;/h3&gt;
&lt;p&gt;In order to be able to upload each commit to the TestPyPI,
we need a different version for each build.
To achieve this, we can use &lt;a href="https://pypi.org/project/hatch-vcs/"&gt;hatch-vcs&lt;/a&gt;, &lt;a href="https://pypi.org/project/setuptools-scm/"&gt;setuptools_scm&lt;/a&gt;, or&amp;nbsp;similar.&lt;/p&gt;
&lt;p&gt;In the following example, we are going to use &lt;a href="https://pypi.org/project/hatchling/"&gt;hatchling&lt;/a&gt; with &lt;a href="https://pypi.org/project/hatch-vcs/"&gt;hatch-vcs&lt;/a&gt; as the build backend and
&lt;a href="https://pypi.org/project/uv/"&gt;uv&lt;/a&gt; for everything&amp;nbsp;else.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We configure the build backend in our &lt;code class="file"&gt;pyproject.toml&lt;/code&gt; as&amp;nbsp;follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[build-system]&lt;/span&gt;
&lt;span class="n"&gt;requires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hatchling&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hatch-vcs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;build-backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;hatchling.build&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;[tool.hatch.version]&lt;/span&gt;
&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;vcs&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;raw-options&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local_scheme&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;no-local-version&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# TestPyPI lacks support for this&lt;/span&gt;

&lt;span class="k"&gt;[project]&lt;/span&gt;
&lt;span class="n"&gt;dynamic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;aside class="admonition hint"&gt;
&lt;p class="admonition-title"&gt;Hint&lt;/p&gt;
&lt;p&gt;Versions with a &lt;a href="https://packaging.python.org/en/latest/specifications/version-specifiers/#local-version-identifiers"&gt;local component&lt;/a&gt; cannot be uploaded to to (Test)PyPI,
so we must disable this&amp;nbsp;feature.&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now lets open our project’s &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt; which we’ll edit during the next&amp;nbsp;steps.&lt;/p&gt;
&lt;aside class="admonition hint"&gt;
&lt;p class="admonition-title"&gt;Hint&lt;/p&gt;
&lt;p&gt;The snippets in the next steps only show fragments of the &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt;.
I’ll post the complete file &lt;a href="#full-gitlab-ci-yml"&gt;at the end&lt;/a&gt; of the&amp;nbsp;article.&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need at least a &lt;code&gt;build&lt;/code&gt; and a &lt;code&gt;deploy&lt;/code&gt; stage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - &amp;#39;test&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - ...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;deploy&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python build tools usually put their artifacts (binary wheels and source distributions) into &lt;code class="file"&gt;dist/&lt;/code&gt;.
This directory needs to be added to your pipeline &lt;em&gt;artifacts&lt;/em&gt;,
so that these files are available in later pipeline&amp;nbsp;jobs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--out-dir=dist&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;dist/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For our use-case, we need two release jobs:
One that uploads to the TestPyPI on each push (&lt;code&gt;release-test&lt;/code&gt;) and
one that uploads to the PyPI in tag pipelines (&lt;code&gt;release&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Since both jobs are nearly the same,
we’ll also define an “abstract base job” &lt;code&gt;.release-base&lt;/code&gt; which the other two&amp;nbsp;extend.&lt;/p&gt;
&lt;aside class="admonition hint"&gt;
&lt;p class="admonition-title"&gt;Hint&lt;/p&gt;
&lt;p&gt;To improve readability and avoid issues with excaping,
we’ll use &lt;span class="caps"&gt;YAML&lt;/span&gt; multiline&amp;nbsp;strings.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;gt;-&lt;/code&gt; operator joins the following lines without a line break and
strips additional&amp;nbsp;whitespace.&lt;/p&gt;
&lt;p&gt;See &lt;a href="https://yaml-multiline.info/"&gt;yaml-multiline.info&lt;/a&gt; for&amp;nbsp;details.&lt;/p&gt;
&lt;/aside&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;.release-base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Abstract base job for &amp;quot;release&amp;quot; jobs.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Extending jobs must define the following variables:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - PYPI_OIDC_AUD: Audience for the ID token that GitLab&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;#   issues to the pipeline job&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - PYPI_OIDC_URL: PyPI endpoint for retrieving a publish&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;#   token with GitLab’s ID token&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - UV_PUBLISH_URL: PyPI endpoint for the actual upload&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;deploy&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;id_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_ID_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$PYPI_OIDC_AUD&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Use the GitLab ID token to retrieve an API token from PyPI&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;&amp;gt;-&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;resp=&amp;quot;$(curl -X POST &amp;quot;${PYPI_OIDC_URL}&amp;quot; -d &amp;quot;{\&amp;quot;token\&amp;quot;:\&amp;quot;${PYPI_ID_TOKEN}\&amp;quot;}&amp;quot;)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Parse the response and extract the token&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;&amp;gt;-&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;publish_token=&amp;quot;$(python -c &amp;quot;import json; print(json.load(&amp;#39;${resp}&amp;#39;)[&amp;#39;token&amp;#39;])&amp;quot;)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Upload the files from &amp;quot;dist/&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$publish_token&amp;quot;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Print the link to PyPI so we can quickly go there to verify the result:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;version=&amp;quot;$(uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hatch-vcs&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hatchling&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;version)&amp;quot;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-e&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\033[34;1mPackage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PyPI:\033[0m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${CI_ENVIRONMENT_URL}${version}/&amp;quot;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now we can add the &lt;code&gt;release-test&lt;/code&gt; job.
It extends &lt;code&gt;.release-base&lt;/code&gt;, defines variables for the base job,
and rules for when the job should&amp;nbsp;run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;release-test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.release-base&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Only run if it&amp;#39;s a pipeline for the default branch or a tag:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_DEFAULT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_TAG&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;release-test&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/project/typed-settings/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_AUD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;testpypi&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/_/oidc/mint-token&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;UV_PUBLISH_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/legacy/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;release&lt;/code&gt; job looks very similar,
but the variables have different values and the job only runs in tag&amp;nbsp;pipelines.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.release-base&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Only run in tag pipelines:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$CI_COMMIT_TAG&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;release&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://pypi.org/project/typed-settings/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_AUD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pypi&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://pypi.org/_/oidc/mint-token&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;UV_PUBLISH_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://upload.pypi.org/legacy/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/release-job-output_dark.png"&gt;&lt;img alt="Screenshot of the &amp;quot;release&amp;quot; job's output in the GitLab CI/CD pipeline." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/release-job-output_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;The output of the &lt;code&gt;release&lt;/code&gt; will look like this.
There’s also a link that takes you directly to the release on PyPI.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/release-job-output_light.png"&gt;&lt;img alt="Screenshot of the &amp;quot;release&amp;quot; job's output in the GitLab CI/CD pipelineents." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/release-job-output_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;The output of the &lt;code&gt;release&lt;/code&gt; will look like this.
There’s also a link that takes you directly to the release on PyPI.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr /&gt;
&lt;p&gt;That’s it.
You should now be able to automatically create PyPI releases directly from your GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline.&amp;nbsp;🎉&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/gitlab-release-pipeline_dark.png"&gt;&lt;img alt="Screenshot of a successful GitLab CI/CD release pipeline." class="only-dark has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/gitlab-release-pipeline_dark.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;A successful GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline for Typed Settings’ v24.6.0 release.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/trusted-publisher/gitlab-release-pipeline_light.png"&gt;&lt;img alt="Screenshot of a successful GitLab CI/CD release pipeline." class="only-light has-shadow-and-caption" src="https://stefan.sofa-rockers.org/images/trusted-publisher/gitlab-release-pipeline_light.png" style="width: 650px; height: 399.5px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;A successful GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline for Typed Settings’ v24.6.0 release.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you run into any problems, you&amp;nbsp;can&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;check if the settings on PyPI match your GitLab&amp;nbsp;project,&lt;/li&gt;
&lt;li&gt;read the &lt;a href="https://docs.pypi.org/trusted-publishers/"&gt;Trusted Publishers&lt;/a&gt;&amp;nbsp;docs,&lt;/li&gt;
&lt;li&gt;read the GitLAB &lt;a href="https://docs.gitlab.com/ee/ci/yaml/"&gt;&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; &lt;span class="caps"&gt;YAML&lt;/span&gt; syntax reference&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;read the docs for &lt;a href="https://docs.gitlab.com/ee/ci/environments/"&gt;GitLab environments&lt;/a&gt; and &lt;a href="https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html"&gt;GitLab &lt;span class="caps"&gt;OIDC&lt;/span&gt; authentication&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;You can leave comments over at &lt;a href="https://mastodon.social/&amp;#64;sscherfke/113479864094276319"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://bsky.app/profile/stefan.sofa-rockers.org/post/3lav7vvddv32x"&gt;Bluesky&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;p id="full-gitlab-ci-yml"&gt;And, as promised, here is the complete (but still minimal) &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt; from the snippets above.
If you want to see a real-world example,
you can take a look at &lt;a href="https://gitlab.com/sscherfke/typed-settings/-/blob/main/.gitlab-ci.yml"&gt;Typed Settings pipeline definition&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="nt"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - &amp;#39;test&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - ...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;deploy&amp;#39;&lt;/span&gt;

&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;build&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--out-dir=dist&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;dist/&amp;#39;&lt;/span&gt;

&lt;span class="nt"&gt;.release-base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Abstract base job for &amp;quot;release&amp;quot; jobs.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Extending jobs must define the following variables:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - PYPI_OIDC_AUD: Audience for the ID token that GitLab issues to the pipeline job&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - PYPI_OIDC_URL: PyPI endpoint for retrieving a publish token with GitLab’s ID token&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# - UV_PUBLISH_URL: PyPI endpoint for the actual upload&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;deploy&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;id_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_ID_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$PYPI_OIDC_AUD&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;&amp;gt;-&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;resp=&amp;quot;$(curl -X POST &amp;quot;${PYPI_OIDC_URL}&amp;quot; -d &amp;quot;{\&amp;quot;token\&amp;quot;:\&amp;quot;${PYPI_ID_TOKEN}\&amp;quot;}&amp;quot;)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;&amp;gt;-&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;publish_token=&amp;quot;$(python -c &amp;quot;import json; print(json.load(&amp;#39;${resp}&amp;#39;)[&amp;#39;token&amp;#39;])&amp;quot;)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$publish_token&amp;quot;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;version=&amp;quot;$(uv&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hatch-vcs&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hatchling&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;version)&amp;quot;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-e&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;\033[34;1mPackage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PyPI:\033[0m&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;${CI_ENVIRONMENT_URL}${version}/&amp;quot;&amp;#39;&lt;/span&gt;

&lt;span class="nt"&gt;release-test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.release-base&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_DEFAULT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;||&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_TAG&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;release-test&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/project/typed-settings/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_AUD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;testpypi&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/_/oidc/mint-token&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;UV_PUBLISH_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://test.pypi.org/legacy/&amp;#39;&lt;/span&gt;

&lt;span class="nt"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;.release-base&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$CI_COMMIT_TAG&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;release&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://pypi.org/project/typed-settings/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_AUD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;pypi&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;PYPI_OIDC_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://pypi.org/_/oidc/mint-token&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;UV_PUBLISH_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;https://upload.pypi.org/legacy/&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
</content><category term="2024"></category><category term="python"></category><category term="packaging"></category><category term="gitlab"></category><category term="pypi"></category></entry><entry><title>Typed Settings</title><link href="https://stefan.sofa-rockers.org/2020/11/30/typed-settings/" rel="alternate"></link><published>2020-11-30T22:14:00+01:00</published><updated>2020-11-30T22:14:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2020-11-30:/2020/11/30/typed-settings/</id><summary type="html">&lt;p&gt;I wrote a new settings library that has advantages and disadvantages
compared to other settings libraries.  With the use cases I have
encountered over the last years, the advantages outweigh the
disadvantages,&amp;nbsp;though.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;There are already several settings libraries like &lt;a href="https://github.com/rochacbruno/dynaconf"&gt;Dynaconf&lt;/a&gt;, &lt;a href="https://github.com/hynek/environ-config"&gt;Environ
Config&lt;/a&gt;, or &lt;a href="https://github.com/samuelcolvin/pydantic"&gt;Pydantic&lt;/a&gt; – just to name a few.  I have written a new one:
&lt;a href="https://typed-settings.readthedocs.io"&gt;Typed Settings&lt;/a&gt;.&lt;/p&gt;
&lt;section id="what-makes-it-different"&gt;
&lt;h2&gt;What makes it&amp;nbsp;different?&lt;/h2&gt;
&lt;p&gt;Settings are defined as type-hinted, immutable (frozen) &lt;a href="https://www.attrs.org/en/stable/"&gt;attrs&lt;/a&gt; classes.
Values are automatically converted to the proper type when they are
loaded.  Apart from simple data types, Typed Settings supports
datetimes, enums, nested attrs classes and various container types (like
lists).  The auto-converter can be extended to handle additional&amp;nbsp;types.&lt;/p&gt;
&lt;p&gt;Settings can be loaded from multiple config files.  Config files can
contain settings for multiple apps (like &lt;code class="file"&gt;pyproject.toml&lt;/code&gt;).
Different deployment environments use different config files (this is in
contrast to Dynaconf, where a single config file specifies the settings
for all environments in different sections).  Currently, only &lt;em&gt;&lt;span class="caps"&gt;TOML&lt;/span&gt;&lt;/em&gt; is
supported.  Support for &lt;em&gt;&lt;span class="caps"&gt;YAML&lt;/span&gt;&lt;/em&gt; or &lt;code class="file"&gt;.env&lt;/code&gt; may follow&amp;nbsp;later.&lt;/p&gt;
&lt;p&gt;Value interpolation as in Dynaconf is not yet supported, but&amp;nbsp;planned.&lt;/p&gt;
&lt;p&gt;Search paths for config files have to be explicitly stated – either
directly in the app or via an environment&amp;nbsp;variable.&lt;/p&gt;
&lt;p&gt;Environment variables can also be used to override settings.  The prefix
is customizable and this feature can also be&amp;nbsp;disabled.&lt;/p&gt;
&lt;p&gt;Finally, Typed Settings can generate &lt;a href="https://click.palletsprojects.com/"&gt;Click&lt;/a&gt; options for command line
applications.  You &lt;span class="caps"&gt;CLI&lt;/span&gt; function will receive all options nicely packed
together as a single instance of your settings&amp;nbsp;class.&lt;/p&gt;
&lt;p&gt;Specialized secrets stores like &lt;a href="https://www.vaultproject.io/"&gt;HashiCorp Vault&lt;/a&gt; are not (yet?)&amp;nbsp;supported.&lt;/p&gt;
&lt;p&gt;Invalid values or undefined options in config files raise an error
instead of being silently ignored.  Config files can optionally be
marked as &lt;em&gt;mandatory&lt;/em&gt; and an error will be raised if such a file cannot
be&amp;nbsp;found.&lt;/p&gt;
&lt;p&gt;To aid with debugging, Typed Settings uses Python’s &lt;a href="https://docs.python.org/3/library/logging.html"&gt;logging module&lt;/a&gt; to
log config files that are being loaded (or that cannot be found) as well
as looked up env&amp;nbsp;vars.&lt;/p&gt;
&lt;p&gt;Everything is thoroughly tested, the test coverage is at&amp;nbsp;100%.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="an-example"&gt;
&lt;h2&gt;An&amp;nbsp;Example&lt;/h2&gt;
&lt;p&gt;Here is a very simple example that demonstrates how you can load
settings from a statically defined config file and from environment&amp;nbsp;variables:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typed_settings&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;ts&lt;/span&gt;

&lt;span class="nd"&gt;@ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;option_one&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;option_two&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

&lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;appname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;config_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;settings.py&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# Paths can also be set via env var&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# settings.toml&lt;/span&gt;
&lt;span class="k"&gt;[example]&lt;/span&gt;
&lt;span class="n"&gt;option_one&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;EXAMPLE_OPTION_TWO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;example.py
&lt;span class="go"&gt;Settings(option_one=&amp;quot;value&amp;quot;, option_two=2)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://gitlab.com/sscherfke/typed-settings#examples"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt; and &lt;a href="https://typed-settings.readthedocs.io/en/latest/examples.html"&gt;documentation&lt;/a&gt; contain more&amp;nbsp;examples.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="project-status"&gt;
&lt;h2&gt;Project&amp;nbsp;Status&lt;/h2&gt;
&lt;p&gt;The recently &lt;a href="https://typed-settings.readthedocs.io/en/latest/changelog.html"&gt;released&lt;/a&gt; &lt;a href="https://pypi.org/project/typed-settings/0.9/"&gt;version 0.9&lt;/a&gt; contains all features that are
planed for version 1.0.0.  Additional features are already &lt;a href="https://gitlab.com/sscherfke/typed-settings/-/milestones/2"&gt;on the
roadmap&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What’s missing for the first stable release is mainly documentation as
well as more real life testing.  I already use Typed Settings for a few
projects in our company and will perspectively try to replace our old
settings system with&amp;nbsp;it.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2020"></category><category term="python"></category><category term="settings"></category><category term="configuration"></category><category term="attrs"></category><category term="click"></category></entry><entry><title>Raise … from … in Python</title><link href="https://stefan.sofa-rockers.org/2020/10/28/raise-from/" rel="alternate"></link><published>2020-10-28T11:23:00+01:00</published><updated>2020-10-28T11:23:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2020-10-28:/2020/10/28/raise-from/</id><summary type="html">&lt;p&gt;Pylint 2.6.0 raises the new warning &lt;em&gt;raise-missing-from&lt;/em&gt;.  What does
that mean?  This post explains Pythons &lt;code&gt;raise ... from ...&lt;/code&gt; syntax
and demonstrates, how you can improve the meaning of re-raised
exceptions by adding their underlying&amp;nbsp;cause.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;When you recently upgraded to &lt;a href="https://pypi.org/project/pylint/2.6.0/"&gt;pylint 2.6.0&lt;/a&gt;, you may have stumbled
across a new&amp;nbsp;warning:&lt;/p&gt;
&lt;pre&gt;src/mylib/core.py:74:20: W0707:
  Consider explicitly re-raising using the
  'from' keyword (raise-missing-from)&lt;/pre&gt;
&lt;p&gt;The reason for this message is an exception that you raised from within
an &lt;code class="code"&gt;except&lt;/code&gt; block like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;Base class for all errors raised by mylib&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;onoes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you run &lt;code class="code"&gt;do_stuff()&lt;/code&gt;, you’ll get the following&amp;nbsp;traceback:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;
&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;onoes&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;During&lt;/span&gt; &lt;span class="n"&gt;handling&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;above&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;another&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="n"&gt;occurred&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;
&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;onoes&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The important line here&amp;nbsp;is:&lt;/p&gt;
&lt;blockquote&gt;
During handling of the above exception, another exception occurred&lt;/blockquote&gt;
&lt;p&gt;This means that while you were handling the &lt;code class="code"&gt;ValueError&lt;/code&gt;, another
(unexpected) exception occurred: a &lt;code class="code"&gt;MyLibError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But this is not what we wanted to do – we wanted to &lt;em&gt;replace&lt;/em&gt; the
&lt;code class="code"&gt;ValueError&lt;/code&gt; with &lt;code class="code"&gt;MyLibError&lt;/code&gt;, so that our uses only have to
handle a single exception&amp;nbsp;type!&lt;/p&gt;
&lt;section id="enter-raise-from"&gt;
&lt;h2&gt;Enter &lt;code&gt;raise … from …&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To express &lt;em&gt;I want to modify and forward an existing exception&lt;/em&gt;, you can
use the &lt;code class="samp"&gt;raise &lt;em&gt;NewException&lt;/em&gt; from &lt;em&gt;cause&lt;/em&gt;&lt;/code&gt; syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;e&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When we run this now, we’ll get a different traceback&amp;nbsp;printed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;onoes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;
&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;onoes&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="n"&gt;above&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;direct&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;following&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;
&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;onoes&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Your users will now receive a &lt;code class="code"&gt;MyLibError&lt;/code&gt; with the attached
information, that the cause of this error was a &lt;code class="code"&gt;ValueError&lt;/code&gt;
somewhere in your&amp;nbsp;code.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="when-the-underlying-cause-is-not-important"&gt;
&lt;h2&gt;When the underlying cause is not&amp;nbsp;important&lt;/h2&gt;
&lt;p&gt;If your users shouldn’t care about the underlying cause, because the
new exception contains all the relevant information (i.e., that the
provided input cannot be parsed), you may also omit the&amp;nbsp;cause:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you run this now, you’ll get a nice an clean&amp;nbsp;traceback:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;onoes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;do_stuff&lt;/span&gt;
&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyLibError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;onoes&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;When you raise an exception from within an &lt;code class="code"&gt;except&lt;/code&gt; block in
Python, you have three&amp;nbsp;options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a new/unexpected exception occurs in the code handling the original
exception, &lt;code class="samp"&gt;raise &lt;em&gt;NewException&lt;/em&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to wrap the original exception(s) (e.g., with a common
base exception to reduce complexity for
your users), &lt;code class="samp"&gt;raise &lt;em&gt;NewException&lt;/em&gt; from &lt;em&gt;cause&lt;/em&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you want to hide the original exception because it is irrelevant
for your users, &lt;code class="samp"&gt;raise &lt;em&gt;NewException&lt;/em&gt; from None&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content><category term="2020"></category><category term="python"></category><category term="error-handling"></category></entry><entry><title>Attrs, Dataclasses and Pydantic</title><link href="https://stefan.sofa-rockers.org/2020/05/29/attrs-dataclasses-pydantic/" rel="alternate"></link><published>2020-05-29T09:36:00+02:00</published><updated>2020-05-29T09:36:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2020-05-29:/2020/05/29/attrs-dataclasses-pydantic/</id><summary type="html">&lt;p&gt;&lt;em&gt;Attrs&lt;/em&gt;, &lt;em&gt;data classes&lt;/em&gt; and &lt;em&gt;pydantic&lt;/em&gt; seem very similar on
a first glance, but they are very different when you take
a closer look.  In this article, I’ll find out, what these
libraries have in common, how they differ and which one I’m
going to use in the&amp;nbsp;future.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I’ve been using &lt;a href="https://www.attrs.org/en/stable/"&gt;attrs&lt;/a&gt; for a long time now and I am really liking it.
It is very flexible, has a nice &lt;span class="caps"&gt;API&lt;/span&gt;, is well documented and maintained,
and has no runtime&amp;nbsp;requirements.&lt;/p&gt;
&lt;p&gt;The main idea behind attrs was to make writing classes with lots of data
attributes (“data classes”) easier.  Instead of&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;spam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eggs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eggs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;eggs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It lets you write&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ib&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;eggs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ib&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Attrs also adds a nice string representation, comparison methods,
optional validation and lots of other stuff to your classes, if you want
to.  You can also opt out of everything; attrs is very&amp;nbsp;flexible.&lt;/p&gt;
&lt;p&gt;Attrs became so popular, that since &lt;a href="https://docs.python.org/3/whatsnew/3.7.html"&gt;Python 3.7&lt;/a&gt; we also have the
&lt;a href="https://docs.python.org/3/library/dataclasses.html"&gt;dataclasses&lt;/a&gt; module in the standard library.  It is predominantly
inspired by attrs (the attrs team was involved in the design of
data classes) but has a smaller feature set and will evolve a lot
slower.  But you can use it out-of-the box without adding a new
requirement to your&amp;nbsp;package.&lt;/p&gt;
&lt;p id="pydantic-recursive-load"&gt;&lt;a href="https://pydantic-docs.helpmanual.io/"&gt;Pydantic&lt;/a&gt;’s development roughly started during Python 3.7 development,
too.  Its main focus is on data validation, settings management and &lt;span class="caps"&gt;JSON&lt;/span&gt;
(de)serialisation, therefore it is located at a higher level ob
abstraction.   Out of the box, it will recursively validate and convert
all data that you pour into your&amp;nbsp;model:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Child&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s1"&gt;&amp;#39;child&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;42&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# sic!&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2020-05-04T13:37:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I only learned about pydantic when I started to work with &lt;a href="https://fastapi.tiangolo.com/"&gt;FastAPI&lt;/a&gt;.
FastAPI is a fast, asynchronous web framework specifically designed for
building &lt;span class="caps"&gt;REST&lt;/span&gt; APIs.  It uses pydantic for schema definition and data&amp;nbsp;validation.&lt;/p&gt;
&lt;p&gt;Since then, I asked myself: Why not attrs?  What’s the benefit of
pydantic over the &lt;a href="https://libraries.io/pypi/attrs/usage"&gt;widely used&lt;/a&gt; and mature attrs?  Can or should it
replace&amp;nbsp;attrs?&lt;/p&gt;
&lt;p&gt;As I begin to write this article, I still don’t know the answer to these
questions.  So lets explore attrs, data classes and&amp;nbsp;pydantic!&lt;/p&gt;
&lt;section id="simple-class-definition"&gt;
&lt;h2&gt;Simple class&amp;nbsp;definition&lt;/h2&gt;
&lt;p&gt;Originally, attrs classes were created by using the &lt;code class="code"&gt;&amp;#64;attr.s()&lt;/code&gt;
(or &lt;code class="code"&gt;&amp;#64;attr.attrs()&lt;/code&gt; class decorator.  Fields had to be created via
the &lt;code class="code"&gt;attr.ib()&lt;/code&gt; (or &lt;code class="code"&gt;&amp;#64;attr.attrib()&lt;/code&gt;) factory function.  By
now, you can also create them nearly like data&amp;nbsp;classes.&lt;/p&gt;
&lt;p&gt;The recommended way for creating pydantic models is to subclass
&lt;code class="code"&gt;pydantic.BaseModel&lt;/code&gt;.  This means that in contrast to data
classes, all models inherit some “public” methods (e.g., for &lt;span class="caps"&gt;JSON&lt;/span&gt;
serialization) which you need to be aware of.  However, pydantic allows
you to create stdlib data classes extended with validation,&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Here are some very simple examples for data classes /&amp;nbsp;models:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;attr&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pydantic&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Simple data classes are supported by all libraries:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# @dataclasses.dataclass&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# @pydantic.dataclasses.dataclass&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ib&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ib&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pydantic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pydantic models enforce keyword only arguments when creating new
instances.  This is a bit tedious for classes with only a few attributes
but with larger models, you’re likely going to use keyword arguments
anyways.  The benefit of kw-only arguments is, that it doesn’t matter if
you list attributes with a default before ones without a&amp;nbsp;default.&lt;/p&gt;
&lt;p&gt;Data classes support positional as well as keyword arguments.  Passing
values by position is very convenient for smaller classes but that also
means that you must define all fields without a default value first and
the ones with a default value afterwards.  This may prevent you from
grouping similar attributes, when only some of them have a default&amp;nbsp;value.&lt;/p&gt;
&lt;p&gt;Attrs supports both ways.  The default is to allow positional and
keyword arguments like data classes.  You can enable kw-only arguments
by passing &lt;code class="code"&gt;kw_only=True&lt;/code&gt; to the class&amp;nbsp;decorator.&lt;/p&gt;
&lt;p&gt;Another major difference is that Pydantic always validates and converts
all attribute values, but more on that&amp;nbsp;later.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="class-and-attribute-customistaion"&gt;
&lt;h2&gt;Class and attribute&amp;nbsp;customistaion&lt;/h2&gt;
&lt;p&gt;All three libraries let you customize the created fields as well as the
class&amp;nbsp;itself.&lt;/p&gt;
&lt;p&gt;In data classes and attrs, you can customize your class by passing
additional arguments to the class decorator.  Pydantic models can define
a nested &lt;a href="https://pydantic-docs.helpmanual.io/usage/model_config/"&gt;Config&lt;/a&gt; class for the same&amp;nbsp;purpose.&lt;/p&gt;
&lt;p&gt;Attributes can be customized via special factory functions.  Instead of
specifying an attribute like this: &lt;code class="code"&gt;name: type [= default]&lt;/code&gt;, you
you do: &lt;code class="code"&gt;name: type = field_factory()&lt;/code&gt;.  This function is named
&lt;code class="code"&gt;[attr.]ib()&lt;/code&gt;/&lt;code class="code"&gt;attrib()&lt;/code&gt; in attrs, &lt;code class="code"&gt;field()&lt;/code&gt; with data
classes and &lt;code class="code"&gt;Field()&lt;/code&gt; in&amp;nbsp;pydantic.&lt;/p&gt;
&lt;p&gt;Using these functions, you can specify default values, validators, meta
data and other attributes.  The following tables let you compare the
customisation features that each library&amp;nbsp;provides:&lt;/p&gt;
&lt;table&gt;
&lt;caption&gt;Attribute / field settings&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
&lt;th&gt;attrs&lt;/th&gt;
&lt;th&gt;data classes&lt;/th&gt;
&lt;th&gt;pydantic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Explicit &lt;em&gt;no default&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class="code"&gt;NOTHING&lt;/code&gt; &lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class="code"&gt;MISSING&lt;/code&gt; &lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class="code"&gt;...&lt;/code&gt; &lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Default factory&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Validators&lt;/td&gt;
&lt;td&gt;yes &lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no &lt;sup&gt;2,3&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Constraints&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;const, regex, length, number range, …&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Converters&lt;/td&gt;
&lt;td&gt;yes &lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no &lt;sup&gt;2,3&lt;/sup&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Exclude field from&lt;/td&gt;
&lt;td&gt;repr, eq, order, hash, init&lt;/td&gt;
&lt;td&gt;repr, compare, hash, init&lt;/td&gt;
&lt;td&gt;ø&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Add arbitrary metadata&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Additional docs&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;title, description&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;aside class="admonition admonition-footnotes"&gt;
&lt;p class="admonition-title"&gt;footnotes&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; Passing &lt;em&gt;no default&lt;/em&gt; is optional in attrs and data classes,
but mandatory in&amp;nbsp;pydatnic.&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;2&lt;/sup&gt; Validators and converters can also be defined as decorated
methods of you&amp;nbsp;class.&lt;/p&gt;
&lt;p&gt;&lt;sup&gt;3&lt;/sup&gt; Pydantic always performs basic validation and conversion for
the attribute’s data type (e.g., &lt;code class="code"&gt;int('42')&lt;/code&gt;).&lt;/p&gt;
&lt;/aside&gt;
&lt;table&gt;
&lt;caption&gt;Class customisation and instantiation&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;
&lt;th&gt;attrs&lt;/th&gt;
&lt;th&gt;data classes&lt;/th&gt;
&lt;th&gt;pydantic&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Auto create methods for&lt;/td&gt;
&lt;td&gt;str, repr, equality, ordering, hash, init&lt;/td&gt;
&lt;td&gt;repr, equality, ordering, hash, init&lt;/td&gt;
&lt;td&gt;str, repr, equality, init&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Keyword args only&lt;/td&gt;
&lt;td&gt;optional&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Faux immutability / Freezing&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Slots&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Safe to subclass exceptions&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Dynamic creation&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Instantiate from dict&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes, recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Instantiate from objects&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;optional, recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Instantiate from &lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes, recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Instantiate from env. vars.&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes, recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;section id="generated-methods"&gt;
&lt;h3&gt;Generated&amp;nbsp;methods&lt;/h3&gt;
&lt;p&gt;All libraries create useful “dunder” methods (like &lt;code class="code"&gt;__init__()&lt;/code&gt; or
&lt;code class="code"&gt;__str__()&lt;/code&gt;).  Attrs can generate the most methods, followed by
data classes and Pydantic.  Attrs and data classes also allow you to
selectively disable the generation of certain&amp;nbsp;methods.&lt;/p&gt;
&lt;p&gt;Attrs is the only library that generates &lt;a href="https://stackoverflow.com/questions/472000/usage-of-slots"&gt;__slots__&lt;/a&gt; and is also the
only one that has explicit support for &lt;a href="https://www.attrs.org/en/stable/api.html#attr.s"&gt;subclassing exceptions&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="default-values"&gt;
&lt;h3&gt;Default&amp;nbsp;values&lt;/h3&gt;
&lt;p&gt;Without a field factory, default values for fields are simply assigned
to the field name, e.g., &lt;code class="code"&gt;value: int = 42&lt;/code&gt;.  When you use a field
factory, you can/need to pass a default value as argument to that
function.  In pydantic, the default value is &lt;em&gt;always&lt;/em&gt; passed as first
positional argument.  In order to express “this attribute has no
default”, you use the &lt;em&gt;elipsis literal&lt;/em&gt; (&lt;code class="code"&gt;...&lt;/code&gt;).  Data classes use
the optional keyword argument &lt;code class="code"&gt;default&lt;/code&gt; instead.  Attrs lets you
choose - you can pass a default value by position or as keyword&amp;nbsp;argument.&lt;/p&gt;
&lt;p&gt;Another difference is that pydantic allows you to use mutable objects
like lists or dicts as default values.  Attrs and data classes prohibit
this &lt;a href="https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments"&gt;for good reason&lt;/a&gt;.  To prevent bugs with mutable defaults,
pydantic deep-copies the default value for each new&amp;nbsp;instance.&lt;/p&gt;
&lt;p&gt;You you can specify factories for default values with all&amp;nbsp;libraries.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="freezing-and-functional-programming"&gt;
&lt;h3&gt;Freezing and functional&amp;nbsp;programming&lt;/h3&gt;
&lt;p&gt;You can create pseudo immutable classes with all libraries.
Immutable/frozen instances prevent you from changing attribute values.
This helps when you aim to program in a more &lt;a href="https://www.johndcook.com/blog/2020/05/15/pretending-oop-never-happened/"&gt;functional style&lt;/a&gt;.
However, if attributes themselves are mutable (like lists or dicts), you
can still change &lt;em&gt;these!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In attrs and data classes, you pass &lt;code class="code"&gt;frozen=True&lt;/code&gt; to the class
decorator.  In pydantic, you set &lt;code class="code"&gt;allow_mutation = False&lt;/code&gt; in the
nested &lt;code class="code"&gt;Config&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Attrs and data classes only generate dunder protocol methods, so your
classes are “clean”.  Having struct-like, frozen instances make it
relatively easy to write &lt;a href="https://stackabuse.com/functional-programming-in-python/"&gt;purely functional code&lt;/a&gt;, that can be more
robust and easier to test than code with a lot of side&amp;nbsp;effects.&lt;/p&gt;
&lt;p&gt;Pydantic models, on the other hand, use inheritance and always have some
methods, e.g., for converting an instance from or to &lt;span class="caps"&gt;JSON&lt;/span&gt;.  This
facilitates a more object-orient programming style, which can be a bit
more convenient in some&amp;nbsp;situations.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="instantiation-validation-and-conversion"&gt;
&lt;h3&gt;Instantiation, validation and&amp;nbsp;conversion&lt;/h3&gt;
&lt;p&gt;The main differentiating features of pydantic are its abilities to
create, validate and serialize&amp;nbsp;classes.&lt;/p&gt;
&lt;p&gt;You can instantiate pydantic models not only from dicts/keyword
arguments but also from other data classes (&lt;a href="https://pydantic-docs.helpmanual.io/usage/models/#orm-mode-aka-arbitrary-class-instances"&gt;&lt;span class="caps"&gt;ORM&lt;/span&gt; mode&lt;/a&gt;), from
&lt;a href="https://pydantic-docs.helpmanual.io/usage/settings/"&gt;environment variables&lt;/a&gt;, and &lt;a href="https://pydantic-docs.helpmanual.io/usage/exporting_models/#custom-json-deserialisation"&gt;raw &lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/a&gt;.  Pydantic will then not only
validate/convert basic data types but also more advanced types like
datetimes.  On top of that, it will recursively create nested model
instances, as shown in the &lt;a href="#pydantic-recursive-load"&gt;example above&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Model instances can directly be exported to dicts and &lt;span class="caps"&gt;JSON&lt;/span&gt; via the
&lt;code class="code"&gt;.dict()&lt;/code&gt;/&lt;code class="code"&gt;.json()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;To achieve something similar in attrs or data classes, you need to
install an &lt;a href="https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs"&gt;extension package&lt;/a&gt; like, for example, &lt;a href="https://cattrs.readthedocs.io/en/latest/usage.html"&gt;cattrs&lt;/a&gt;.  And even
then, Pydantic has a far better user&amp;nbsp;experience.&lt;/p&gt;
&lt;p&gt;Apart from that, all libraries allow you to define custom validator and
converter functions.  You can either pass these functions to the field
factories or define decorated methods in your&amp;nbsp;class.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="metadata-and-schema-generation"&gt;
&lt;h3&gt;Metadata and schema&amp;nbsp;generation&lt;/h3&gt;
&lt;p&gt;Pydantic can not only serialize model instances but also the schema of
the model classes themselves.  This is, for example, used by FastAPI to
generate the &lt;a href="https://www.openapis.org"&gt;OpenAPI&lt;/a&gt; spec for an &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To aid the documentation of the generated schemas, every field can have
a &lt;em&gt;title&lt;/em&gt; and a &lt;em&gt;description&lt;/em&gt; attribute.  These are not used for
docstrings,&amp;nbsp;though.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="documentation"&gt;
&lt;h2&gt;Documentation&lt;/h2&gt;
&lt;p&gt;In a way, the documentation of all three projects mirrors their
feature&amp;nbsp;sets.&lt;/p&gt;
&lt;p&gt;It is of high quality in all cases, but technically and in terms of
content very&amp;nbsp;different.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/dataclasses.html"&gt;data classes documentation&lt;/a&gt; is part of Python’s stdlib
documentation and the briefest of all candidates, but it covers
everything you need to know.  It contains a direct link to the source
code that also has many helpful&amp;nbsp;comments.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.attrs.org/en/stable/index.html"&gt;attrs docs&lt;/a&gt; contain example based guides, in-depth discussions of
certain features and design decisions as well as an exhaustive &lt;span class="caps"&gt;API&lt;/span&gt;
reference.  It uses &lt;a href="https://www.sphinx-doc.org/en/master/"&gt;Sphinx&lt;/a&gt; and, for the &lt;span class="caps"&gt;API&lt;/span&gt; reference, the &lt;a href="https://www.sphinx-doc.org/en/master/usage/quickstart.html#autodoc"&gt;autodoc&lt;/a&gt;
extension.  It provides an &lt;a href="http://attrs.readthedocs.io/objects.inv"&gt;objects inventory&lt;/a&gt; which allows you to
cross-reference attrs classes and functions from your own documentation
via &lt;a href="https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html"&gt;intersphinx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://pydantic-docs.helpmanual.io/"&gt;pydantic documentation&lt;/a&gt; is also very well written and contains
many good examples that explain almost all functionality in great
detail.  However, it follows the unfortunate trend of using &lt;a href="https://www.mkdocs.org/"&gt;MkDocs&lt;/a&gt; as
a documentation system.  I assume that this is easier to set-up then
Sphinx and allows you to use Markdown instead of ReStructuredText, but
it is also lacking lots of important features and I also don’t like its
&lt;span class="caps"&gt;UX&lt;/span&gt;.  It has two navigation menus – one on the left for whole document’s
&lt;span class="caps"&gt;TOC&lt;/span&gt; and one on the right for the current page.  More serious, however,
is the absence of an &lt;span class="caps"&gt;API&lt;/span&gt; reference.  There is also no cross referencing
(e.g., links from class and function names to their section in the &lt;span class="caps"&gt;API&lt;/span&gt;
reference) and thus no objects inventory that can be used for
inter-project cross referencing via Sphinx’ &lt;a href="https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html"&gt;intersphinx&lt;/a&gt; extension.
Even pydantic’s source code barely includes any docstrings or other
comments.  This can be a hindrance when you want to solve more
advanced&amp;nbsp;problems.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;An alternative for MkDocs might be &lt;a href="https://myst-parser.readthedocs.io/en/latest/"&gt;MyST&lt;/a&gt;, which is an extend Markdown
parser that can be used with&amp;nbsp;Sphinx.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/tomchristie/mkautodoc"&gt;MkAutoDoc&lt;/a&gt; allows you to add an &lt;span class="caps"&gt;API&lt;/span&gt; ref. to MkDocs.  It does not
(yet?) generate an &lt;code class="file"&gt;objects.inv&lt;/code&gt; for use with intersphinx,&amp;nbsp;though.&lt;/p&gt;
&lt;p&gt;&lt;code class="file"&gt;objects.inv&lt;/code&gt; also enables &lt;a href="https://github.com/hynek/doc2dash"&gt;doc2dash&lt;/a&gt; which in turn enables
&lt;a href="https://kapeli.com/dash"&gt;Dash&lt;/a&gt; and &lt;a href="https://zealdocs.org/"&gt;Zeal&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="performance"&gt;
&lt;h2&gt;Performance&lt;/h2&gt;
&lt;p&gt;For most use cases, the performance of a data classes library can be
neglected.  Performance differences only become noticeable when you
create thousands or even millions of instances in a short amount of&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;However, the pydantic docs contain &lt;a href="https://pydantic-docs.helpmanual.io/benchmarks/"&gt;some benchmarks&lt;/a&gt; that suggest that
pydantic is slightly ahead of attrs + cattrs in mean validation time.
I was curious why pydantic, despite its larger feature set, was so fast,
so I made my own&amp;nbsp;benchmarks.&lt;/p&gt;
&lt;p&gt;I briefly evaluate the &lt;a href="https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs"&gt;attrs extension packages&lt;/a&gt;.  The only one that
offers reasonably convenient means for input validation and
(de)serialization as well as good performance is &lt;a href="https://cattrs.readthedocs.io/en/latest/usage.html"&gt;cattrs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I created benchmarks for three different use&amp;nbsp;cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Simple classes and no need for extensive&amp;nbsp;validation&lt;/li&gt;
&lt;li&gt;Deserialization and validation of (more) complex (nested)&amp;nbsp;classes&lt;/li&gt;
&lt;li&gt;Serialization of complex (nested)&amp;nbsp;classes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I calculated the time and memory consumption for handling 1 million
instances of different variants of attrs and data classes as well as
pydantic&amp;nbsp;models.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, attrs and data classes are much faster than pydantic
when no validation is needed.  They also use a lot less&amp;nbsp;memory.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Relative time and memory consumption for basic class instantiation" src="https://stefan.sofa-rockers.org/images/dcbenchmark_basic.png" style="width: 512px; height: 308px" /&gt;
&lt;/figure&gt;
&lt;p&gt;I was expecting that the results would be much closer when it comes to
validation/conversion and serialization, but even there, pydantic was
a lot slower than attrs +&amp;nbsp;cattrs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Relative time and memory consumption for loading more complex, nested classes" src="https://stefan.sofa-rockers.org/images/dcbenchmark_load.png" style="width: 512px; height: 170px" /&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;img alt="Relative time and memory consumption for dumping more complex, nested classes" src="https://stefan.sofa-rockers.org/images/dcbenchmark_dump.png" style="width: 512px; height: 130px" /&gt;
&lt;/figure&gt;
&lt;p&gt;I wondered why my benchmarks were so clearly in favor of attrs when the
pydantic docs state that it is 1.4x faster than attrs + cattrs.  I tried
running the pydantic benchmarks myself and indeed I could reproduce
these results.  Wondering, why the results differed so much, I took
a closer look at the benchmark’s source code.  It turned out that the
attrs + cattrs example used &lt;a href="https://pypi.org/project/python-dateutil/"&gt;python-dateutil&lt;/a&gt; for parsing datetimes
while pydantic uses its own implementation.  I replaced
&lt;code class="code"&gt;datetuil.parser.parse()&lt;/code&gt; with the stdlib
&lt;code class="code"&gt;datetuil.fromisoformat()&lt;/code&gt; and the attrs + cattrs example suddenly
became 6–7 times faster. &lt;em&gt;(Note:&lt;/em&gt; &lt;code class="code"&gt;fromisoformat()&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat"&gt;is not&lt;/a&gt;
a general purpose&amp;nbsp;parser!)&lt;/p&gt;
&lt;p&gt;In defense of pydantic: The attrs + cattrs (de)serializers were
specifically designed and implemented for this benchmark while Pydantic
ships everything out-of-the box.  Pydantic’s &lt;span class="caps"&gt;UX&lt;/span&gt; for these use cases is
also more pleasant than that of attrs +&amp;nbsp;cattrs.&lt;/p&gt;
&lt;p&gt;You can find the source of all benchmark in the &lt;a href="https://gitlab.com/sscherfke/article-code/-/tree/master/2020/attrs-dataclasses-pydantic*"&gt;accompanying
repository&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Attrs, data classes and pydantic seem very similar on a first glance,
but they are very different when you take a closer&amp;nbsp;look.&lt;/p&gt;
&lt;p&gt;All three projects are of high quality, well documented and generally
pleasant to use.  Furthermore, they are different enough that each of
them has its niche where it really&amp;nbsp;shines.&lt;/p&gt;
&lt;p&gt;The stdlib’s data classes module provides all you need for simple use
cases and it does not add a new requirement to your project.  Since it
does not do any data validation, it is also quite&amp;nbsp;fast.&lt;/p&gt;
&lt;p&gt;When you need more features (more control over the generated class or
data validation and conversion), you should use attrs.  It is as fast as
data classes are but its memory footprint is even smaller when you
enable &lt;code class="code"&gt;__slots__&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you need extended input validation and data conversion, Pydantic is
the tool of choice.  The price of its many features and nice &lt;span class="caps"&gt;UX&lt;/span&gt; is
a comparatively bad performance,&amp;nbsp;though.&lt;/p&gt;
&lt;p&gt;If you want to cut back on &lt;span class="caps"&gt;UX&lt;/span&gt; instead, the combination of attrs and
cattrs might also be an&amp;nbsp;alternative.&lt;/p&gt;
&lt;p&gt;You can take a look at the &lt;a href="https://gitlab.com/sscherfke/article-code/-/tree/master/2020/attrs-dataclasses-pydantic"&gt;benchmarks&lt;/a&gt; to get a feel for how the
libraries can be used for different use cases and how they differ form
each&amp;nbsp;other.&lt;/p&gt;
&lt;p&gt;I myself will stay with attrs as long as it can provide what I need.
Otherwise I’ll use&amp;nbsp;Pydantic.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="epilogue"&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;I wish there was a library like &lt;em&gt;mattrs (magic attrs)&lt;/em&gt; that combined
Pydantic’s (de)serialization &lt;span class="caps"&gt;UX&lt;/span&gt; with attrs’ niceness and&amp;nbsp;performance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;mattrs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asjson&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^[a-z0-9-]+$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Child&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;child&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;42&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;2020-05-04T13:37:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;asjson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;spam&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;child&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;y&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;d&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2020-05-04T13:37:00&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Maybe it’s time for another side project?&amp;nbsp;🙊&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 1:&lt;/strong&gt; Added a few links about documentation&amp;nbsp;systems&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; Fixed chart for benchmark&amp;nbsp;#3&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
</content><category term="2020"></category><category term="python"></category></entry><entry><title>Packaging Python inside your organization with GitLab and Conda</title><link href="https://stefan.sofa-rockers.org/2019/04/18/python-packaging-gitlab-conda/" rel="alternate"></link><published>2019-04-18T13:37:00+02:00</published><updated>2019-04-18T13:37:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2019-04-18:/2019/04/18/python-packaging-gitlab-conda/</id><summary type="html">&lt;p&gt;Conda, GitLab and Docker can be used to manage and publish non-&lt;span class="caps"&gt;OSS&lt;/span&gt;
code, e.g. inside organizations.  You need some additional tooling
for a smooth user experience,&amp;nbsp;though.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://packaging.python.org/"&gt;Python Packaging&lt;/a&gt; &lt;a href="http://andrewsforge.com/article/python-new-package-landscape/"&gt;has recently&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=18247512"&gt;been&lt;/a&gt;
&lt;a href="https://github.com/orsinium/notes/blob/master/notes-en/python-packaging.md"&gt;discussed&lt;/a&gt; &lt;a href="https://hynek.me/articles/python-app-deps-2018/"&gt;a lot&lt;/a&gt;, but the articles usually
only focus on publishing (open source) code to&amp;nbsp;PyPI.&lt;/p&gt;
&lt;p&gt;But what do you do when your organization uses Python for in-house development
and you can’t (or don’t want to) make everything Open Source?  Where do you
store and manage your code?  How do you distribute your&amp;nbsp;packages?&lt;/p&gt;
&lt;p&gt;In this article, I describe how we solve this problem with &lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt;, &lt;a href="https://conda.io"&gt;Conda&lt;/a&gt; and a few other&amp;nbsp;tools.&lt;/p&gt;
&lt;p&gt;You can find all code and examples referenced in this article under
&lt;a href="https://gitlab.com/ownconda"&gt;gitlab.com/ownconda&lt;/a&gt;.  These tools and examples are using the &lt;code&gt;own&lt;/code&gt; prefix
in order to make a clear distinction between our own and third-party code.
I will not necessarily update and fix the code, but it is released under the
&lt;a href="https://blueoakcouncil.org/license/1.0.0"&gt;Blue Oak license&lt;/a&gt; so you can copy and use it.  Any feedback is welcome,&amp;nbsp;nonetheless.&lt;/p&gt;
&lt;nav&gt;
&lt;h3&gt;Contents:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#software-selection" id="toc-entry-1"&gt;Software selection&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#code-and-issue-management" id="toc-entry-2"&gt;Code and issue&amp;nbsp;management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-package-manager-pip-or-conda" id="toc-entry-3"&gt;The package manager: Pip or&amp;nbsp;Conda?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#supplementary-tools" id="toc-entry-4"&gt;Supplementary&amp;nbsp;tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#how-it-should-work" id="toc-entry-5"&gt;How it should work&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#runtime-and-build-environment" id="toc-entry-6"&gt;Runtime and build&amp;nbsp;environment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#rd-party-packages" id="toc-entry-7"&gt;3rd party&amp;nbsp;packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#internal-projects" id="toc-entry-8"&gt;Internal&amp;nbsp;projects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#package-and-documentation-hosting" id="toc-entry-9"&gt;Package and documentation&amp;nbsp;hosting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#python-distribution" id="toc-entry-10"&gt;Python&amp;nbsp;distribution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#deployment" id="toc-entry-11"&gt;Deployment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#dependency-management" id="toc-entry-12"&gt;Dependency&amp;nbsp;management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#making-it-work" id="toc-entry-13"&gt;Making it work&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="#set-up-gitlab-and-a-conda-repo-server" id="toc-entry-14"&gt;Set up GitLab and a Conda repo&amp;nbsp;server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#bootstrap-python-pip-and-conda" id="toc-entry-15"&gt;Bootstrap Python, Pip and&amp;nbsp;Conda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#build-the-docker-images" id="toc-entry-16"&gt;Build the docker&amp;nbsp;images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#build-all-packages" id="toc-entry-17"&gt;Build all&amp;nbsp;packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#outlook-future-work-and-unsolved-problems" id="toc-entry-18"&gt;Outlook / Future work and unsolved&amp;nbsp;problems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;section id="software-selection"&gt;
&lt;h2&gt;&lt;a href="#toc-entry-1"&gt;Software&amp;nbsp;selection&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this section I’ll briefly explain the reasons why we are using GitLab and&amp;nbsp;Conda.&lt;/p&gt;
&lt;section id="code-and-issue-management"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-2"&gt;Code and issue&amp;nbsp;management&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Though you could use private repositories from one of the well-known cloud
services, you should probably use a self-hosted service to retain full control
over your code.  In some countries it may even be forbidden to use a &lt;span class="caps"&gt;US&lt;/span&gt; cloud
service for your organization’s&amp;nbsp;data.&lt;/p&gt;
&lt;p&gt;There are &lt;a href="https://www.cyberciti.biz/open-source/github-alternatives-open-source-seflt-hosted/"&gt;plenty of competitors&lt;/a&gt; in this field: &lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt;, &lt;a href="https://gitea.io/"&gt;Gitea&lt;/a&gt;, &lt;a href="https://gogs.io/"&gt;Gogs&lt;/a&gt;,
&lt;a href="https://github.com/gitbucket/gitbucket/"&gt;Gitbucket&lt;/a&gt; or &lt;a href="https://kallithea-scm.org/"&gt;Kallithea&lt;/a&gt;—just to name a&amp;nbsp;few.&lt;/p&gt;
&lt;p&gt;Our most important requirements&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repository&amp;nbsp;management&lt;/li&gt;
&lt;li&gt;Pull/Merge&amp;nbsp;requests&lt;/li&gt;
&lt;li&gt;Issue&amp;nbsp;management&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;&amp;nbsp;pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only tool that (currently) meets these requirements is GitLab.  It has
a lot more features that are very useful for an organization wide use, e.g.,
&lt;span class="caps"&gt;LDAP&lt;/span&gt; and Kerberos support, issue labels and boards, Mattermost integration or
Git &lt;span class="caps"&gt;LFS&lt;/span&gt; support.  And—more importantly—it also has a really nice &lt;span class="caps"&gt;UX&lt;/span&gt; and is
one of the few pieces of software that I actually enjoy&amp;nbsp;using.&lt;/p&gt;
&lt;p&gt;GitLab has a &lt;a href="https://about.gitlab.com/pricing/#self-managed"&gt;free core&lt;/a&gt; and some paid versions that add &lt;a href="https://about.gitlab.com/pricing/self-managed/feature-comparison/"&gt;more features&lt;/a&gt; and&amp;nbsp;support.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-package-manager-pip-or-conda"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-3"&gt;The package manager: Pip or&amp;nbsp;Conda?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://pip.pypa.io/"&gt;Pip&lt;/a&gt; is the &lt;a href="https://packaging.python.org/guides/tool-recommendations/"&gt;official&lt;/a&gt; package installer for Python.  It supports Python
source distributions and (binary) &lt;em&gt;Wheel&lt;/em&gt; packages.  Pip only installs files in
the current environment’s &lt;code class="file"&gt;site-packages&lt;/code&gt; directory and can optionally
create entry points in its &lt;code class="file"&gt;bin&lt;/code&gt; directory.  You can use &lt;a href="https://virtualenv.pypa.io/en/latest/"&gt;Virtualenv&lt;/a&gt; to
isolate different projects from another, and &lt;a href="https://devpi.net/docs/devpi/devpi/stable/%2Bd/index.html"&gt;Devpi&lt;/a&gt; to host your own package
index.  Devpi can both, &lt;a href="https://stefan.sofa-rockers.org/2017/11/09/getting-started-with-devpi/"&gt;mirror/cache PyPI and store your own packages&lt;/a&gt;.  The Python packaging ecosystem is overlooked by the Python
Packaging Authority working group (&lt;a href="https://www.pypa.io/"&gt;PyPA&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://conda.io"&gt;Conda&lt;/a&gt; stems from the scientific community and is being developed by &lt;a href="https://www.anaconda.com"&gt;Anaconda&lt;/a&gt;.
In contrast to Pip, Conda is a full-fledged package manager similar to
&lt;strong&gt;apt&lt;/strong&gt; or &lt;strong&gt;dnf&lt;/strong&gt;.  Like virtualenv, Conda can create isolated
virtual environments.  Conda is not directly compatible with Python’s
&lt;code class="file"&gt;setup.py&lt;/code&gt; or &lt;code class="file"&gt;pyproject.toml&lt;/code&gt; files.  Instead, you have to create
a Conda recipe for every package and build it with &lt;strong&gt;conda-build&lt;/strong&gt;.
This is a bit more involved because you have to convert every package that you
find on PyPI, but it also lets you patch and extend every package.  With very
little effort you can create a self-extracting Python distribution with
a selection of custom packages (similar to the &lt;a href="https://conda.io/en/latest/miniconda.html"&gt;Miniconda&lt;/a&gt;&amp;nbsp;distribution).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://conda-forge.org"&gt;Conda-forge&lt;/a&gt; is a (relatively) new project that has a huge library of Conda
recipes and packages.  However, if you want full control over your own packages
you may want to host and build everything on your&amp;nbsp;own.&lt;/p&gt;
&lt;section id="what-to-use"&gt;
&lt;h4&gt;What to&amp;nbsp;use?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Both, Conda and pip, allow you to host your own packages as well as 3rd party
packages inside your&amp;nbsp;organization.&lt;/li&gt;
&lt;li&gt;Both, Conda and pip, provide isolated virtual&amp;nbsp;environments.&lt;/li&gt;
&lt;li&gt;Conda can package anything (Python, C-libraries, Rust apps, …) while Pip is
exclusively for Python&amp;nbsp;packages.&lt;/li&gt;
&lt;li&gt;With Conda, you need to package and build everything on your own.  Even
packages from PyPI need to be re-packaged.  On the other side, this makes it
easier to patch and extend the package’s&amp;nbsp;source.&lt;/li&gt;
&lt;li&gt;Newer Conda versions allow you to build &lt;em&gt;everything&lt;/em&gt; on your own, even &lt;span class="caps"&gt;GCC&lt;/span&gt;
and libc.  This is, however, not required and you can rely on some low-level
system libraries like the &lt;a href="https://github.com/pypa/manylinux"&gt;manylinux standard&lt;/a&gt; for Wheels does. (You just
have to decide which ones, but more on that&amp;nbsp;later.)&lt;/li&gt;
&lt;li&gt;Due to its larger scope, Conda is slower and more complex than Pip.  In the
past, even patch releases introduced backwards incompatible changes and bugs
that broke our stack.  However, the devs are very friendly and usually fix
critical bugs quite fast.  And maybe we would have had similar problems, too,
if we used a Pip based&amp;nbsp;stack.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because we need to package more than just Python, we chose to use Conda.  This
dates back to at least to Conda v2.1 which was released in 2013.  At that time,
projects like conda-forge weren’t even in&amp;nbsp;sight.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="supplementary-tools"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-4"&gt;Supplementary&amp;nbsp;tools&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To aid our work with GitLab and Conda, we developed some supplementary tools.
I have released a slightly modified version of them, called &lt;a href="https://gitlab.com/ownconda/ownconda-tools"&gt;ownconda tools&lt;/a&gt;,
alongside with this&amp;nbsp;article.&lt;/p&gt;
&lt;p&gt;The ownconda tools are a &lt;a href="https://click.palletsprojects.com"&gt;click&lt;/a&gt; based collection of commands that reside under
the entry point &lt;strong&gt;ownconda&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Initially, they were only meant to help with the management of recipes for
external packages, and with running the build/test/upload steps in our GitLab
pipeline.  But they have become a lot more powerful by now and even include
a GitLab Runner that lets you run your projects’ pipelines locally (including
artifacts handling, which the official gitlab-runner cannot do&amp;nbsp;locally).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;span class="go"&gt;Usage: ownconda [OPTIONS] COMMAND [ARGS]...&lt;/span&gt;

&lt;span class="go"&gt;  Support tools for local development, CI/CD and Conda packaging.&lt;/span&gt;

&lt;span class="go"&gt;Options:&lt;/span&gt;
&lt;span class="go"&gt;  --help  Show this message and exit.&lt;/span&gt;

&lt;span class="go"&gt;Commands:&lt;/span&gt;
&lt;span class="go"&gt;  build                 Build all recipes in RECIPE_ROOT in the correct...&lt;/span&gt;
&lt;span class="go"&gt;  check-for-updates     Update check for external packages in RECIPE_ROOT.&lt;/span&gt;
&lt;span class="go"&gt;  ci                    Run a GitLab CI pipeline locally.&lt;/span&gt;
&lt;span class="go"&gt;  completion            Print Bash or ZSH completion activation script.&lt;/span&gt;
&lt;span class="go"&gt;  dep-graph             Create a dependency graph from a number of Conda...&lt;/span&gt;
&lt;span class="go"&gt;  develop               Install PATHS in develop/editable mode.&lt;/span&gt;
&lt;span class="go"&gt;  gitlab                Run a task on a number of GitLab projects.&lt;/span&gt;
&lt;span class="go"&gt;  lint                  Run pylint for PATHS.&lt;/span&gt;
&lt;span class="go"&gt;  make-docs             Run sphinx-build and upload generated html...&lt;/span&gt;
&lt;span class="go"&gt;  prune-index           Delete old packages from the local Conda index at...&lt;/span&gt;
&lt;span class="go"&gt;  pylintrc              Print the built-in pylintrc to stdout.&lt;/span&gt;
&lt;span class="go"&gt;  pypi-recipe           Create or update recipes for PyPI packages.&lt;/span&gt;
&lt;span class="go"&gt;  sec-check             Run some security checks for PATHS.&lt;/span&gt;
&lt;span class="go"&gt;  show-updated-recipes  Show updated recipes in RECIPE_ROOT.&lt;/span&gt;
&lt;span class="go"&gt;  test                  Run tests in PATHS.&lt;/span&gt;
&lt;span class="go"&gt;  update-recipes        Update Conda recipes in RECIPE_ROOT.&lt;/span&gt;
&lt;span class="go"&gt;  upload                Upload Conda packages in PKG_DIR.&lt;/span&gt;
&lt;span class="go"&gt;  validate-recipes      Check if recipes in RECIPE_ROOT are valid.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I will talk about the various subcommands in more detail in later&amp;nbsp;sections.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="how-it-should-work"&gt;
&lt;h2&gt;&lt;a href="#toc-entry-5"&gt;How it should&amp;nbsp;work&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The subject of packaging consists of several components: The platforms on which
your code needs to build and run, the package manager and repository,
management of external and internal packages, a custom Python distribution, and
means to keep an overview over all packages and their dependencies.  I will go
into detail about each aspect in the following&amp;nbsp;sections.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-overview.png"&gt;&lt;img alt="Aspects involved in the topic of packaging" src="https://stefan.sofa-rockers.org/images/packaging-overview.png" style="width: 555px; height: 501px" /&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;section id="runtime-and-build-environment"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-6"&gt;Runtime and build&amp;nbsp;environment&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our packages need to run on Fedora desktop systems and on Centos 7.  Packages
built on Centos also run on Fedora, so we only have a single build environment:
Centos&amp;nbsp;7.&lt;/p&gt;
&lt;p&gt;We use different Docker images for our build pipeline and some deployments.
The most important ones are &lt;a href="https://gitlab.com/ownconda/centos7-ownconda-runtime"&gt;centos7-ownconda-runtime&lt;/a&gt; and
&lt;a href="https://gitlab.com/ownconda/centos7-ownconda-develop"&gt;centos7-ownconda-develop&lt;/a&gt;.  The former only contains a minimal setup to
install and run Conda packages while the latter includes all build
dependencies, &lt;strong&gt;conda-build&lt;/strong&gt; and the ownconda&amp;nbsp;tools.&lt;/p&gt;
&lt;p&gt;If your &lt;span class="caps"&gt;OS&lt;/span&gt; landscape is more heterogeneous, you may need to add more build
environments which makes things a bit more complicated—especially if you need
to support macOS or even&amp;nbsp;Windows.&lt;/p&gt;
&lt;p&gt;To build Docker images in our GitLab pipelines, we use &lt;em&gt;docker-in-docker&lt;/em&gt;.
That means that the GitLab runners start docker containers that can access
&lt;code class="file"&gt;/var/run/dockers.sock&lt;/code&gt; to run &lt;code&gt;docker build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;GitLab provides a Docker registry that allows any project to host its own
images.  However, if a project is &lt;em&gt;private&lt;/em&gt;, other project’s pipelines can not
access these images.  For this reason, we have decided to serve Docker images
from a separate&amp;nbsp;host.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="rd-party-packages"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-7"&gt;3rd party&amp;nbsp;packages&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We re-package all external dependencies as Conda packages and host them in our
own Conda&amp;nbsp;repository.&lt;/p&gt;
&lt;p&gt;This has several&amp;nbsp;benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can prohibit installing Software from other sources than our internal
Conda&amp;nbsp;repository.&lt;/li&gt;
&lt;li&gt;If users want to depend on new libraries, we can propose alternatives that we
might already have on our index.  This keeps our tree of dependencies a bit&amp;nbsp;smaller.&lt;/li&gt;
&lt;li&gt;We cannot accidentally depend on packages with “bad”&amp;nbsp;licenses.&lt;/li&gt;
&lt;li&gt;We can add patches to fix bugs or extend the functionality of a package
(e.g., we added our internal root certificate to&amp;nbsp;Certifi).&lt;/li&gt;
&lt;li&gt;We can reduce network traffic to external servers and are less dependent on
their&amp;nbsp;availability.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="recipe-organization"&gt;
&lt;h4&gt;Recipe&amp;nbsp;organization&lt;/h4&gt;
&lt;p&gt;We can either put the recipe for every package into its own repository (which
is what &lt;a href="https://conda-forge.org"&gt;conda-forge&lt;/a&gt; does) or use a single repository for all recipes (which is
what we are&amp;nbsp;doing).&lt;/p&gt;
&lt;p&gt;The multi-repository approach makes it easier to only build packages that have
changed.  It also makes it easier to manage access levels if you have a lot of
contributors that each only manage a few&amp;nbsp;packages.&lt;/p&gt;
&lt;p&gt;The single-repository approach has less overhead if you only have a few
maintainers that take care of all the recipes.  To identify updated packages
that need re-building, we can use &lt;strong&gt;ownconda&lt;/strong&gt;’s &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/show_updated_recipes.py"&gt;show-updated-recipes&lt;/a&gt;&amp;nbsp;command.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="linking-against-system-packages"&gt;
&lt;h4&gt;Linking against system&amp;nbsp;packages&lt;/h4&gt;
&lt;p&gt;With Conda, we can (and must) decide whether we want to link against system
packages (e.g., installed with &lt;strong&gt;yum&lt;/strong&gt; or use other Conda packages to
satisfy a package’s&amp;nbsp;dependencies.&lt;/p&gt;
&lt;p&gt;One extreme would be to only build Python packages on our own and completely
depend on system packages for all C libraries.  The other extreme would be to
build &lt;em&gt;everything&lt;/em&gt; on our own, even glibc and&amp;nbsp;gcc.&lt;/p&gt;
&lt;p&gt;The former has a lot less overhead but becomes the more fragile the more
heterogeneous your runtime environments become.  The latter is a lot more
complicated and involved but gives you more control and&amp;nbsp;reliability.&lt;/p&gt;
&lt;p&gt;We decided to take the middle ground between these two extremes: We build many
libraries on our own but rely on the system’s &lt;strong&gt;gcc&lt;/strong&gt;, &lt;strong&gt;glibc&lt;/strong&gt;,
and &lt;strong&gt;X11&lt;/strong&gt; libraries.  This is quite similar to what the &lt;a href="https://github.com/pypa/manylinux"&gt;manylinux
standard&lt;/a&gt; for Python Wheels&amp;nbsp;does.&lt;/p&gt;
&lt;p&gt;Recipes must list the system libraries that they link against.  The rules for
valid system libraries are encoded in &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/6715e48e/src/own_conda_tools/commands/validate_recipes.py#L38"&gt;ownconda validate-recipes&lt;/a&gt; and enforced
by &lt;strong&gt;conda-build&lt;/strong&gt;’s &lt;a href="https://docs.conda.io/projects/conda-build/en/latest/resources/commands/conda-build.html?highlight=--error-overlinking"&gt;–error-overlinking&lt;/a&gt;&amp;nbsp;option.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="recipe-management"&gt;
&lt;h4&gt;Recipe&amp;nbsp;management&lt;/h4&gt;
&lt;p&gt;Recipes for Python packages can easily be created with &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/pypi_recipe.py"&gt;ownconda pypi-recipe&lt;/a&gt;.
This is similar to &lt;a href="https://docs.conda.io/projects/conda-build/en/latest/user-guide/tutorials/build-pkgs-skeleton.html#building-a-simple-package-with-conda-skeleton-pypi"&gt;conda skeleton pypi&lt;/a&gt; but tailored to our needs.  Recipes
for other packages have to be created&amp;nbsp;manually.&lt;/p&gt;
&lt;p&gt;We also implemented an update check for our recipes.  Every recipe contains
a script called &lt;code class="file"&gt;update_check.py&lt;/code&gt; which uses one of the update checkers
provided by the ownconda&amp;nbsp;tools.&lt;/p&gt;
&lt;p&gt;These checkers can query PyPI, GitHub release lists and (&lt;span class="caps"&gt;FTP&lt;/span&gt;) directory
listings, or crawl an entire website.  The command &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/check_for_updates.py"&gt;ownconda
check-for-updates&lt;/a&gt; runs the update scripts and compares the version numbers
they find against the recipes’ current versions.  It can also print URLs to
the packages’&amp;nbsp;changelogs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;check-for-updates&lt;span class="w"&gt; &lt;/span&gt;--verbose&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="go"&gt;  [████████████████████████████████████]  100%&lt;/span&gt;
&lt;span class="go"&gt;Package: latest version (current version)&lt;/span&gt;
&lt;span class="go"&gt;freetype 2.10.0 (2.9.1):&lt;/span&gt;
&lt;span class="go"&gt;  https://www.freetype.org/index.html#news&lt;/span&gt;

&lt;span class="go"&gt;python-attrs 19.1.0 (18.2.0):&lt;/span&gt;
&lt;span class="go"&gt;  http://www.attrs.org/en/stable/changelog.html&lt;/span&gt;

&lt;span class="go"&gt;python-certifi 2019.3.9 (2018.11.29):&lt;/span&gt;
&lt;span class="go"&gt;  https://github.com/certifi/python-certifi/commits/master&lt;/span&gt;

&lt;span class="go"&gt;...&lt;/span&gt;

&lt;span class="go"&gt;qt5 5.12.2 (5.12.1):&lt;/span&gt;
&lt;span class="go"&gt;  https://wiki.qt.io/Qt_5.12.2_Change_Files&lt;/span&gt;

&lt;span class="go"&gt;readline 8.0.0 (7.0.5):&lt;/span&gt;
&lt;span class="go"&gt;  https://tiswww.case.edu/php/chet/readline/CHANGES&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can then update all recipes with &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/update_recipes.py"&gt;ownconda update-recipes&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;update-recipes&lt;span class="w"&gt; &lt;/span&gt;python-attrs&lt;span class="w"&gt; &lt;/span&gt;...
&lt;span class="go"&gt;python-attrs&lt;/span&gt;
&lt;span class="go"&gt;cd /data/ssd/home/stefan/Projects/ownconda/external-recipes &amp;amp;&amp;amp; /home/stefan/ownconda/bin/python -m own_conda_tools pypi-recipe attrs -u&lt;/span&gt;
&lt;span class="go"&gt;diff --git a/python-attrs/meta.yaml b/python-attrs/meta.yaml&lt;/span&gt;
&lt;span class="go"&gt;index 7d167a8..9b3ea20 100644&lt;/span&gt;
&lt;span class="go"&gt;--- a/python-attrs/meta.yaml&lt;/span&gt;
&lt;span class="go"&gt;+++ b/python-attrs/meta.yaml&lt;/span&gt;
&lt;span class="go"&gt;@@ -1,10 +1,10 @@&lt;/span&gt;
&lt;span class="go"&gt; package:&lt;/span&gt;
&lt;span class="go"&gt;  name: attrs&lt;/span&gt;
&lt;span class="go"&gt;-  version: 18.2.0&lt;/span&gt;
&lt;span class="go"&gt;+  version: 19.1.0&lt;/span&gt;

&lt;span class="go"&gt; source:&lt;/span&gt;
&lt;span class="go"&gt;-  url: https://files.pythonhosted.org/packages/0f/9e/26b1d194aab960063b266170e53c39f73ea0d0d3f5ce23313e0ec8ee9bdf/attrs-18.2.0.tar.gz&lt;/span&gt;
&lt;span class="go"&gt;-  sha256: 10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69&lt;/span&gt;
&lt;span class="go"&gt;+  url: https://files.pythonhosted.org/packages/cc/d9/931a24cc5394f19383fbbe3e1147a0291276afa43a0dc3ed0d6cd9fda813/attrs-19.1.0.tar.gz&lt;/span&gt;
&lt;span class="go"&gt;+  sha256: f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399&lt;/span&gt;

&lt;span class="go"&gt; build:&lt;/span&gt;
&lt;span class="go"&gt;-  number: 1&lt;/span&gt;
&lt;span class="go"&gt;+  number: 0&lt;/span&gt;

&lt;span class="go"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="the-update-process"&gt;
&lt;h4&gt;The update&amp;nbsp;process&lt;/h4&gt;
&lt;p&gt;Our Conda repository has various channels for packages of different maturity,
e.g. &lt;strong class="channel"&gt;experimental&lt;/strong&gt;, &lt;strong class="channel"&gt;testing&lt;/strong&gt;, &lt;strong class="channel"&gt;staging&lt;/strong&gt;, and
&lt;strong class="channel"&gt;stable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Updates are first built locally and uploaded to the &lt;strong class="channel"&gt;testing&lt;/strong&gt; channel
for some manual&amp;nbsp;testing.&lt;/p&gt;
&lt;p&gt;If everything goes well, the updates are committed into the &lt;code class="branch"&gt;develop&lt;/code&gt;
branch, pushed to GitLab and uploaded to the &lt;strong class="channel"&gt;staging&lt;/strong&gt; channel.  We
also send a &lt;a href="https://gitlab.com/ownconda/external-recipes/blob/master/updates.rst"&gt;changelog&lt;/a&gt; around to notify everyone about important
updates and when they will be uploaded into the &lt;strong class="channel"&gt;stable&lt;/strong&gt;&amp;nbsp;channel.&lt;/p&gt;
&lt;p&gt;After a few days in testing, the updates are merged into the &lt;code class="branch"&gt;master&lt;/code&gt;
branch and upload to the &lt;strong class="channel"&gt;stable&lt;/strong&gt; channel for production&amp;nbsp;use.&lt;/p&gt;
&lt;p&gt;This is a relatively save procedure which (usually) catches any problems before
they go into&amp;nbsp;production.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example-recipes"&gt;
&lt;h4&gt;Example&amp;nbsp;recipes&lt;/h4&gt;
&lt;p&gt;You can find the recipes for all packages required to run the ownconda tools
&lt;a href="https://gitlab.com/ownconda/external-recipes"&gt;here&lt;/a&gt;.  As a bonus, I also added the recipes for NumPy
and&amp;nbsp;PyQt5.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="internal-projects"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-8"&gt;Internal&amp;nbsp;projects&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Internal packages are structured in a similar way to most projects that you see
on PyPI.  We put the source code into &lt;code class="file"&gt;src&lt;/code&gt;, the &lt;a href="https://pytest.org"&gt;pytest&lt;/a&gt; tests into
&lt;code class="file"&gt;tests&lt;/code&gt; and the &lt;a href="https://www.sphinx-doc.org"&gt;Sphinx&lt;/a&gt; docs into &lt;code class="file"&gt;docs&lt;/code&gt;.  We do not use namespace
packages.  They can lead to &lt;a href="https://specs.openstack.org/openstack/oslo-specs/specs/kilo/drop-namespace-packages.html"&gt;various nasty bugs&lt;/a&gt;.  Instead, we just prefix all
packages with &lt;code&gt;own_&lt;/code&gt; to avoid name clashes with other packages and to easily
tell internal and external packages&amp;nbsp;apart.&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-package.png"&gt;&lt;img alt="A project usually has the folloing files and directories: .gitignore, .gitlab-ci.yml, conda/meta.yaml, setup.py, setup.cfg, MANIFEST.in, docs/, src/, tests/" src="https://stefan.sofa-rockers.org/images/packaging-package.png" style="width: 195px; height: 267" /&gt;&lt;/a&gt;
&lt;figcaption&gt;A project usually contains at least these files and directories.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The biggest difference to “normal” Python projects is the additional Conda
recipe in each project.  It contains all meta data and the requirements.  The
&lt;code class="file"&gt;setup.py&lt;/code&gt; contains only the minimum amount of information to get the
package installed via &lt;strong&gt;pip&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Conda-build&lt;/strong&gt; runs it to build the Conda&amp;nbsp;package.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/develop.py"&gt;ownconda develop&lt;/a&gt; runs it to install the package in editable&amp;nbsp;mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;ownconda develop&lt;/strong&gt; also creates/updates a Conda
environment for the current project and installs all requirements that it
collects from the project’s&amp;nbsp;recipe.&lt;/p&gt;
&lt;p&gt;Projects also contain a &lt;code class="file"&gt;.gitlab-ci.yml&lt;/code&gt; which defines the &lt;a href="https://docs.gitlab.com/ce/ci/"&gt;GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt;
pipeline&lt;/a&gt;.  Most projects have at least a &lt;em&gt;build&lt;/em&gt;, a &lt;em&gt;test&lt;/em&gt; and an &lt;em&gt;upload&lt;/em&gt;
stage.  The &lt;em&gt;test&lt;/em&gt; stage is split into parallel steps for various test tools
(e.g., &lt;a href="https://pytest.org"&gt;pytest&lt;/a&gt;, &lt;a href="https://www.pylint.org/"&gt;pylint&lt;/a&gt; and &lt;a href="https://bandit.readthedocs.io/en/latest/"&gt;bandit&lt;/a&gt;).  Projects can optionally build
documentation and upload it to our docs server.  The ownconda tools provide
helpers for all of these&amp;nbsp;steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/build.py"&gt;ownconda build&lt;/a&gt; builds the&amp;nbsp;package.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/test.py"&gt;ownconda test&lt;/a&gt; runs&amp;nbsp;pytest.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/lint.py"&gt;ownconda lint&lt;/a&gt; runs&amp;nbsp;pylint.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/sec_check.py"&gt;ownconda sec-check&lt;/a&gt; runs&amp;nbsp;bandit.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/upload.py"&gt;ownconda upload&lt;/a&gt; uploads the package to the package&amp;nbsp;index.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/make_docs.py"&gt;ownconda make-docs&lt;/a&gt; builds and uploads the&amp;nbsp;documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also use our own Git&amp;nbsp;flow:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-git-flow.png"&gt;&lt;img alt="Visualisation of our Git flow" src="https://stefan.sofa-rockers.org/images/packaging-git-flow.png" style="width: 650px; height: 500px" /&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Development happens in a &lt;code class="branch"&gt;develop&lt;/code&gt; branch.  Builds from this branch
are uploaded into a &lt;strong class="channel"&gt;staging&lt;/strong&gt; Conda&amp;nbsp;channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Larger features can optionally branch of a feature branch.  Their builds are
not uploaded into a public Conda&amp;nbsp;channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stable &lt;code class="branch"&gt;develop&lt;/code&gt; states get merged into the &lt;strong class="channel"&gt;master&lt;/strong&gt; branch.
Builds are uploaded into our &lt;strong class="channel"&gt;stable&lt;/strong&gt; Conda&amp;nbsp;channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since we continuously deploy packages, we don’t put a lot of effort into
versioning.  The package version consists of a major release which rarely
changes and the number of commits since the last tagged major release.  The
GitLab pipeline &lt;span class="caps"&gt;ID&lt;/span&gt; is used as a build&amp;nbsp;number:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Version: &lt;code class="samp"&gt;&lt;em&gt;$GIT_DESCRIBE_TAG&lt;/em&gt;.&lt;em&gt;$GIT_DESCRIBE_NUMBER&lt;/em&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Build: &lt;code class="samp"&gt;py37_&lt;em&gt;$CI_PIPELINE_ID&lt;/em&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The required values are automatically exported by Conda and GitLab as
environment&amp;nbsp;variables.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="package-and-documentation-hosting"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-9"&gt;Package and documentation&amp;nbsp;hosting&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hosting a Conda repository is very easy.  In fact, you can just run &lt;code&gt;python -m
http.server&lt;/code&gt; in your local Conda base directory if you previously built any
packages.  You can then use it like this: &lt;code class="samp"&gt;conda search
--override-channels --channel=http://localhost:8000/conda-bld &lt;em&gt;PKG&lt;/em&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A Conda repository consists of one or more &lt;em&gt;channels&lt;/em&gt;.  Each channel is
a directory that contains a &lt;code class="file"&gt;noarch&lt;/code&gt; directory and additional platform
directories (like &lt;code class="file"&gt;linux-64&lt;/code&gt;).  You put your packages into these
directories and run &lt;code class="samp"&gt;conda index &lt;em&gt;channel&lt;/em&gt;/&lt;em&gt;platform&lt;/em&gt;&lt;/code&gt; to create an index
for each platform (you can omit the &lt;code class="samp"&gt;&lt;em&gt;platform&lt;/em&gt;&lt;/code&gt; with newer versions of
&lt;strong&gt;conda-build&lt;/strong&gt;).  The &lt;code class="file"&gt;noarch&lt;/code&gt; directory must always exist, even
if you put all your packages into the &lt;code class="file"&gt;linux-64&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;The base &lt;span class="caps"&gt;URL&lt;/span&gt; for our Conda channels is
&lt;code class="samp"&gt;https://forge.services.own/conda/&lt;em&gt;channel&lt;/em&gt;&lt;/code&gt;.  You can put a static
&lt;code class="file"&gt;index.html&lt;/code&gt; into each channel’s directory that parses the repo data and
displays it&amp;nbsp;nicely:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-forge.png"&gt;&lt;img alt="Forge channel view.  A JavaScript reads and renders the contents of the repodata.json." src="https://stefan.sofa-rockers.org/images/packaging-forge.png" style="width: 650px; height: 574px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;A JavaScript reads and renders the contents of a channel’s
&lt;code class="file"&gt;repodata.json&lt;/code&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The upload service (for packages created in GitLab pipelines) resides under
&lt;code&gt;https://forge.services.own/upload/&amp;lt;channel&amp;gt;&lt;/code&gt;.  It is a simple web
application that stores the uploaded file in &lt;code class="file"&gt;channel/linux-64&lt;/code&gt; and runs
&lt;code&gt;conda index&lt;/code&gt;.  For packages uploaded to the &lt;strong class="channel"&gt;stable&lt;/strong&gt; channel, it
also creates a hard link in a special &lt;strong class="channel"&gt;archive&lt;/strong&gt;&amp;nbsp;channel.&lt;/p&gt;
&lt;p&gt;Every week, we prune our channels with &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/prune_index.py"&gt;ownconda prune-index&lt;/a&gt;.  In case that
we accidentally prune too aggressively, we have the option to restore packages
from the&amp;nbsp;archive.&lt;/p&gt;
&lt;p&gt;We also host our own &lt;a href="https://readthedocs.org/"&gt;Read the Docs&lt;/a&gt; like service.  GitLab pipelines can
upload Sphinx documentation to &lt;code&gt;https://forge.services.own/docs&lt;/code&gt; via
&lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/make_docs.py"&gt;ownconda make-docs&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The server name &lt;em&gt;forge&lt;/em&gt; does not refer to &lt;em&gt;conda-forge&lt;/em&gt; but to
&lt;a href="https://sourceforge.net/"&gt;SourceForge.net&lt;/a&gt;, which was quite popular back in the&amp;nbsp;days.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="python-distribution"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-10"&gt;Python&amp;nbsp;distribution&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With &lt;a href="https://github.com/conda/constructor"&gt;Constructor&lt;/a&gt;, you can easily create your own self-extractable Python
distribution.  These distributions are similar to &lt;a href="https://conda.io/en/latest/miniconda.html"&gt;miniconda&lt;/a&gt;, but you can
customize them to your&amp;nbsp;needs.&lt;/p&gt;
&lt;p&gt;A constructor file is a simple &lt;span class="caps"&gt;YAML&lt;/span&gt; file with some meta data (e.g., the
distribution name and version) and the list of packages that should be
included.  You can also specify a post-install&amp;nbsp;script.&lt;/p&gt;
&lt;p&gt;The command &lt;code&gt;constructor &amp;lt;distdir&amp;gt;/construct.yaml&lt;/code&gt; will then download all
packages and put them into a self extracting Bash script.  We upload the
installer scripts onto our Conda index,&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;Instead of managing multiple &lt;code class="file"&gt;construct.yaml&lt;/code&gt; files manually, we create
them &lt;a href="https://gitlab.com/ownconda/ownconda-dist"&gt;dynamically in a GitLab pipeline&lt;/a&gt; which makes
building multiple similar distributions (e.g., for different Python versions)
a bit&amp;nbsp;easier.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="deployment"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-11"&gt;Deployment&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We are currently on the road from &lt;em&gt;copy-stuff-with-fabric-to-vms&lt;/em&gt; to
&lt;em&gt;docker-kubernetes-yay-land&lt;/em&gt;.  I am not going to go too much into detail
here—this topic is not directly related to packaging and worth its own&amp;nbsp;article.&lt;/p&gt;
&lt;p&gt;Most of our deployments are now &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; based.  Projects contain an
&lt;code class="file"&gt;ansible&lt;/code&gt; directory with the required playbooks and other files.  Shared
roles are managed in a separate &lt;em&gt;ownsible&lt;/em&gt; project.  The ansible deployments
are usually part of the GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;/&lt;span class="caps"&gt;CD&lt;/span&gt; pipeline.  Some are run automatically,
some need to be triggered&amp;nbsp;manually.&lt;/p&gt;
&lt;p&gt;Some newer projects are already using Docker based deployments.  Docker images
are built as part of the pipeline and uploaded into our Docker registry from
which they are then pulled for&amp;nbsp;deployments.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dependency-management"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-12"&gt;Dependency&amp;nbsp;management&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is very helpful if you can build a dependency graph of all your&amp;nbsp;packages.&lt;/p&gt;
&lt;p&gt;Not only can it be used to build all packages in the correct order (as we will
shortly see), but visualizing your dependencies may also help you to improve
your architecture, detect circular dependencies or unused&amp;nbsp;packages.&lt;/p&gt;
&lt;p&gt;The command &lt;a href="https://gitlab.com/ownconda/ownconda-tools/blob/master/src/own_conda_tools/commands/dep_graph.py"&gt;ownconda dep-graph&lt;/a&gt; builds such a dependency graph from the
packages that you pass to it.  It can either output a sorted list of packages
or a &lt;a href="https://en.wikipedia.org/wiki/DOT_(graph_description_language)"&gt;&lt;span class="caps"&gt;DOT&lt;/span&gt;&lt;/a&gt; graph.  Since the resulting graph can become quite large, there are
several ways to filter packages.  For example, you can only show a package’s
dependencies or why the package is&amp;nbsp;needed.&lt;/p&gt;
&lt;p&gt;The following figure shows the dependency graph for our &lt;strong&gt;python&lt;/strong&gt; recipe.  It
was created with the command &lt;code class="samp"&gt;ownconda dep-graph external-recipes/
--implicit --requirements python --out=dot &amp;gt; deps_python.dot&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-deps-python.svg"&gt;&lt;img alt="Dependency graph for Python" src="https://stefan.sofa-rockers.org/images/packaging-deps-python.svg" style="width: 616px; height: 253px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Dependency graph for Python&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These graphs can become quite unclear relatively fast, though.  This is the
full dependency graph for the ownconda&amp;nbsp;tools:&lt;/p&gt;
&lt;figure&gt;
&lt;a href="https://stefan.sofa-rockers.org/images/packaging-deps-ownconda-tools.svg"&gt;&lt;img alt="Dependency graph for the ownconda tools" src="https://stefan.sofa-rockers.org/images/packaging-deps-ownconda-tools.svg" style="width: 616px; height: 87px" /&gt;&lt;/a&gt;
&lt;figcaption&gt;Dependency graph for the ownconda tools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I do not want to know how this would have looked if these were all JavaScript
packages&amp;nbsp;…&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="making-it-work"&gt;
&lt;h2&gt;&lt;a href="#toc-entry-13"&gt;Making it&amp;nbsp;work&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that you know the theory of how everything should work, we can start to
bootstrap our packaging&amp;nbsp;infrastructure.&lt;/p&gt;
&lt;p&gt;Some of the required steps are a bit laborious and you may need the assistance
of your &lt;span class="caps"&gt;IT&lt;/span&gt; department in order to set up the domains and GitLab.  Other steps
can be automated and should be relatively painless,&amp;nbsp;though:&lt;/p&gt;
&lt;section id="set-up-gitlab-and-a-conda-repo-server"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-14"&gt;Set up GitLab and a Conda repo&amp;nbsp;server&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://about.gitlab.com/install/"&gt;Install GitLab&lt;/a&gt;.  I’ll assume that it will be available under
&lt;code&gt;https://git.services.own&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup the forge server.  I’ll assume that it will be available under
&lt;code&gt;https://forge.services.own&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In your www root, create a &lt;code class="file"&gt;conda&lt;/code&gt; folder which will contain the
channels and their&amp;nbsp;packages.&lt;/li&gt;
&lt;li&gt;Create the upload service that copies files sent to
&lt;code class="samp"&gt;/upload/&lt;em&gt;channel&lt;/em&gt;&lt;/code&gt; into &lt;code class="samp"&gt;&lt;em&gt;www-root&lt;/em&gt;/conda/&lt;em&gt;channel&lt;/em&gt;/linux-64&lt;/code&gt;
and calls &lt;code&gt;conda index&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Setup a &lt;a href="https://docs.docker.com/registry/deploying/"&gt;Docker registry&lt;/a&gt; on the&amp;nbsp;server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="bootstrap-python-pip-and-conda"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-15"&gt;Bootstrap Python, Pip and&amp;nbsp;Conda&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Clone all repositories that you need for the bootstrapping&amp;nbsp;process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;~/Projects/ownconda
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/Projects/ownconda
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;r&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;external-recipes&lt;span class="w"&gt; &lt;/span&gt;ownconda-tools&lt;span class="w"&gt; &lt;/span&gt;ownconda-dist&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;git@gitlab.com:ownconda/&lt;span class="nv"&gt;$r&lt;/span&gt;.git&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build all packages needed to create your Conda distribution.  The ownconda
tools provide a script that uses a Docker container to build all packages
and upload them into the &lt;strong class="channel"&gt;stable&lt;/strong&gt;&amp;nbsp;channel:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda-tools/contrib/bootstrap.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The script might fail to build some packages.  The most probable causes
are &lt;span class="caps"&gt;HTTP&lt;/span&gt; timeouts or unavailable servers.  Just re-run the script and
hope for the best.  If the issue persists, you might need to fix the
corresponding Conda recipe, though (Sometimes, people re-upload a source
archive and thereby change its &lt;span class="caps"&gt;SHA256&lt;/span&gt;&amp;nbsp;value).&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the initial Conda distributions and upload&amp;nbsp;them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ownconda-dist
&lt;span class="gp"&gt;$ &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;gen_installer.py&lt;span class="w"&gt; &lt;/span&gt;..&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.7
&lt;span class="gp"&gt;$ &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;gen_installer.py&lt;span class="w"&gt; &lt;/span&gt;..&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.7&lt;span class="w"&gt; &lt;/span&gt;dev
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-
&lt;span class="gp"&gt;$ &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-F&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file=@ownconda-3.7.sh&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;https://forge.services.own/upload/stable
&lt;span class="gp"&gt;$ &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-F&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file=@ownconda-3.7-dev.sh&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;https://forge.services.own/upload/stable
&lt;span class="gp"&gt;$&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="c1"&gt;# Create symlinks for more convenience:&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;forge.services.own
&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;www-root/conda/stable
&lt;span class="gp"&gt;# &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;linux-64/ownconda-3.7.sh
&lt;span class="gp"&gt;# &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;linux-64/ownconda-3.7.sh&lt;span class="w"&gt; &lt;/span&gt;ownconda.sh
&lt;span class="gp"&gt;# &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;linux-64/ownconda-3.7-dev.sh
&lt;span class="gp"&gt;# &lt;/span&gt;ln&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;linux-64/ownconda-3.7-dev.sh&lt;span class="w"&gt; &lt;/span&gt;ownconda-dev.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can now download the installers from
&lt;code class="samp"&gt;https://forge.services.own/conda/stable/ownconda[-dev][-3.7].sh&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup your local ownconda environment.  You can use the installer that you
just built (or (re)download it from the forge if you want to test&amp;nbsp;it):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;ownconda-3.7.sh
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="c1"&gt;# or:&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/Downloads
&lt;span class="gp"&gt;$ &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;https://forge.services.own/conda/stable/ownconda-dev.sh
&lt;span class="gp"&gt;$ &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;ownconda-dev.sh
&lt;span class="gp"&gt;$&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/.bashrc&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# or open a new terminal&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;conda&lt;span class="w"&gt; &lt;/span&gt;info
&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="build-the-docker-images"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-16"&gt;Build the docker&amp;nbsp;images&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create a GitLab pipeline for the &lt;strong&gt;centos7-ownconda-runtime&lt;/strong&gt; project.  This
will generate your runtime Docker&amp;nbsp;image.&lt;/li&gt;
&lt;li&gt;When the runtime image is available, create a GitLab pipeline for the
&lt;strong&gt;centos7-ownconda-develop&lt;/strong&gt; project.  This will generate your development
Docker image used in your projects’&amp;nbsp;pipelines.&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="build-all-packages"&gt;
&lt;h3&gt;&lt;a href="#toc-entry-17"&gt;Build all&amp;nbsp;packages&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create a GitLab pipeline for the &lt;strong&gt;external-recipes&lt;/strong&gt; project to build and
upload the remaining 3rd party&amp;nbsp;packages.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can now build the packages for your internal projects.  You must create
the pipelines in dependency order so that the requirements for each project
are built first.  The ownconda tools help you with&amp;nbsp;that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;gl-projects
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gl-projects
&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;gitlab&lt;span class="w"&gt; &lt;/span&gt;update
&lt;span class="gp"&gt;$ &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;dep-graph&lt;span class="w"&gt; &lt;/span&gt;--no-third-party&lt;span class="w"&gt; &lt;/span&gt;--out&lt;span class="o"&gt;=&lt;/span&gt;project&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;project.txt
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;projects.txt&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;ownconda&lt;span class="w"&gt; &lt;/span&gt;gitlab&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run-py&lt;span class="w"&gt; &lt;/span&gt;../ownconda-tools/contrib/gl_run_pipeline.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If a pipeline fails and the script aborts, just remove the successful
projects from the &lt;code class="file"&gt;projects.txt&lt;/code&gt; and re-run the &lt;code&gt;for&lt;/code&gt; loop.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Congratulations, you are done!  You have built all internal and external
packages, you have created your own Conda distribution and you have all Docker
images that you need for running and building your&amp;nbsp;packages.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="outlook-future-work-and-unsolved-problems"&gt;
&lt;h2&gt;&lt;a href="#toc-entry-18"&gt;Outlook / Future work and unsolved&amp;nbsp;problems&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing your organization’s packaging infrastructure like this is a whole lot
of work but it rewards you with a lot of independence, control and&amp;nbsp;flexibility.&lt;/p&gt;
&lt;p&gt;We have been continuously improving our process during the last years and still
have a lot of ideas on our&amp;nbsp;roadmap.&lt;/p&gt;
&lt;p&gt;While, for example, GitLab has a very good authentication and authorization
system, our Conda repository lacks all of this (apart from &lt;span class="caps"&gt;IP&lt;/span&gt; restrictions for
uploading and downloading packages).  We do not want users (or automated
scripts) to enter credentials when they install or update packages, but we are
not aware of a (working) password-less alternative.  Combining Conda with
Kerberos might work in theory, but in practice this is not yet possible.
Currently, we are experimenting with &lt;span class="caps"&gt;HTTPS&lt;/span&gt; client certificates.  This might
work well enough but it also doesn’t seem to be the Holy Grail of Conda&amp;nbsp;Authorization.&lt;/p&gt;
&lt;p&gt;Another big issue is creating more reproducible builds and easier rollback
mechanisms in case an update ships broken code.  Currently, we are pinning the
requirements’ versions during a pipelines &lt;em&gt;test&lt;/em&gt; stage.  We are also working
towards dockerized Blue Green Deployments and are exploring tools for container
orchestration (like &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;).  On the other hand, we are still delivering
&lt;span class="caps"&gt;GUI&lt;/span&gt; applications to client workstations via Bash scripts … (this works quite
well, though, and provides us with a good amount of control and&amp;nbsp;flexibility).&lt;/p&gt;
&lt;p&gt;We are also still having an eye on Pip.  Conda has the biggest benefits when
deploying packages to VMs and client workstations.  The more we use docker, the
smaller the benefit &lt;em&gt;might&lt;/em&gt; become, and we &lt;em&gt;might&lt;/em&gt; eventually switch back to&amp;nbsp;Pip.&lt;/p&gt;
&lt;p&gt;But for now, Conda serves us very&amp;nbsp;well.&lt;/p&gt;
&lt;aside class="admonition admonition-comments"&gt;
&lt;p class="admonition-title"&gt;Comments&lt;/p&gt;
&lt;p&gt;You can leave comments and suggestions at &lt;a href="https://news.ycombinator.com/item?id=19690280"&gt;Hacker News&lt;/a&gt; and &lt;a href="https://www.reddit.com/r/Python/comments/bekm8c/packaging_python_inside_your_organization_with/"&gt;Reddit&lt;/a&gt; or reach
me via &lt;a href="https://twitter.com/sscherfke/status/1118846079565488128"&gt;Twitter&lt;/a&gt; and &lt;a href="https://mastodon.social/web/statuses/101947106538744845"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
</content><category term="2019"></category><category term="python"></category><category term="packaging"></category><category term="conda"></category><category term="gitlab"></category><category term="docker"></category></entry><entry><title>The macOS Dark Mode, your Terminal and Vim</title><link href="https://stefan.sofa-rockers.org/2018/10/23/macos-dark-mode-terminal-vim/" rel="alternate"></link><published>2018-10-23T22:24:00+02:00</published><updated>2018-10-23T22:24:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2018-10-23:/2018/10/23/macos-dark-mode-terminal-vim/</id><summary type="html">&lt;p&gt;MacOS’ new Dark Mode is quite nice, but as a developer, you might
also want to switch your Terminal profile and Vim theme – all at the
same time, of course&amp;nbsp;…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;The new &lt;em&gt;Dark Mode&lt;/em&gt; in macOS Mojave is a nice addition and is – especially in
the night hours — more pleasing to your eyes than the light&amp;nbsp;mode.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="MacOS light mode with a light Terminal profile and a light Vim theme." src="https://stefan.sofa-rockers.org/images/macos-dark-mode-1.png" style="width: 512px; height: 320px" /&gt;
&lt;figcaption&gt;MacOS light mode with a light Terminal profile and a light Vim theme.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, enabling Dark Mode will not change the Terminal profile, which is
a little bit annoying – especially if your color theme has a light and a dark
variant (like the infamous &lt;a href="https://github.com/altercation/vim-colors-solarized"&gt;Solarized&lt;/a&gt;, &lt;a href="https://github.com/nightsense/snow"&gt;Snow&lt;/a&gt;, &lt;a href="https://github.com/rakr/vim-one"&gt;One&lt;/a&gt;, or my own &lt;a href="https://gitlab.com/sscherfke/dotfiles/blob/master/"&gt;Rasta&lt;/a&gt;&amp;nbsp;theme).&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="MacOS dark mode with a light Terminal profile and a light Vim theme." src="https://stefan.sofa-rockers.org/images/macos-dark-mode-2.png" style="width: 512px; height: 320px" /&gt;
&lt;figcaption&gt;MacOS dark mode with a light Terminal profile and a light Vim theme.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you change your Terminal profile to something dark, Vim still doesn’t look
right because it uses its own mechanism for light/dark backgrounds (see &lt;code&gt;:help
'background'&lt;/code&gt; for details) and doesn’t  know about the changes you made to the
Terminal&amp;nbsp;profile.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="MacOS dark mode with a dark Terminal profile and a light Vim theme." src="https://stefan.sofa-rockers.org/images/macos-dark-mode-3.png" style="width: 512px; height: 320px" /&gt;
&lt;figcaption&gt;MacOS dark mode with a dark Terminal profile and a light Vim theme.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you execute &lt;code&gt;:set background=dark&lt;/code&gt; in Vim (and if you color scheme
supports it), Vim looks nice and dark now,&amp;nbsp;too.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="MacOS dark mode with a dark Terminal profile and a dark Vim theme." src="https://stefan.sofa-rockers.org/images/macos-dark-mode-4.png" style="width: 512px; height: 320px" /&gt;
&lt;figcaption&gt;MacOS dark mode with a dark Terminal profile and a dark Vim theme.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, on the next day, the fun begins again when you want to switch
everything back to light mode&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Wouldn’t it be nice if this could all be accomplished with a single&amp;nbsp;command?&lt;/p&gt;
&lt;p&gt;There are tools, that help you with switching to/from macOS Dark Mode (e.g.,
&lt;a href="https://nightowl.kramser.xyz/"&gt;NightOwl&lt;/a&gt; or &lt;a href="https://shifty.natethompson.io/en/"&gt;Shifty&lt;/a&gt;), but they can’t change your Terminal profile or notify&amp;nbsp;Vim.&lt;/p&gt;
&lt;p&gt;As it turns out, it’s not too hard to implement a little program that does
exactly&amp;nbsp;this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You can uses the &lt;code&gt;defaults&lt;/code&gt; command to get the current macOS Dark Theme&amp;nbsp;mode:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;defaults&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;AppleInterfaceStyle
&lt;span class="go"&gt;Dark&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can use AppleScript (oh, how I love this language …) to set Dark Mode and
update the Terminal&amp;nbsp;profile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;# Set Dark Mode&lt;/span&gt;
&lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nb"&gt;application&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;System Events&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nv"&gt;appearance&lt;/span&gt; &lt;span class="nv"&gt;preferences&lt;/span&gt;
        &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;dark&lt;/span&gt; &lt;span class="nv"&gt;mode&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;  &lt;span class="c"&gt;# Can be one of: true, false, not dark&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;

&lt;span class="c"&gt;# Update default settings (for new windows/tabs)&lt;/span&gt;
&lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nb"&gt;application&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Terminal&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;default&lt;/span&gt; &lt;span class="nv"&gt;settings&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;settings&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Rasta&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;

&lt;span class="c"&gt;# Update settings for exsting windows/tabs&lt;/span&gt;
&lt;span class="k"&gt;tell&lt;/span&gt; &lt;span class="nb"&gt;application&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Terminal&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nv"&gt;current&lt;/span&gt; &lt;span class="nv"&gt;settings&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nv"&gt;tabs&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;windows&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;settings&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Rasta&amp;quot;&lt;/span&gt;  &lt;span class="c"&gt;# Theme name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;tell&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can wrap both things with a Python&amp;nbsp;script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# toggle-macos-dark-mode.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;


&lt;span class="n"&gt;OSASCRIPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;tell application &amp;quot;System Events&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    tell appearance preferences&lt;/span&gt;
&lt;span class="s2"&gt;        set dark mode to &lt;/span&gt;&lt;span class="si"&gt;{mode}&lt;/span&gt;
&lt;span class="s2"&gt;    end tell&lt;/span&gt;
&lt;span class="s2"&gt;end tell&lt;/span&gt;

&lt;span class="s2"&gt;tell application &amp;quot;Terminal&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    set default settings to settings set &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{theme}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;end tell&lt;/span&gt;

&lt;span class="s2"&gt;tell application &amp;quot;Terminal&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;    set current settings of tabs of windows to settings set &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{theme}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;end tell&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="n"&gt;TERMINAL_THEMES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rasta light&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Rasta&amp;#39;&lt;/span&gt;&lt;span class="p"&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;is_dark_mode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return the current Dark Mode status.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;defaults&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;read&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-g&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;AppleInterfaceStyle&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&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;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dark&amp;#39;&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_interface_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Enable/disable dark mode.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;false&amp;#39;&lt;/span&gt;  &lt;span class="c1"&gt;# mode can be {true, false, not dark}&lt;/span&gt;
    &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OSASCRIPT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TERMINAL_THEMES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;osascript&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-e&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;set_interface_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_dark_mode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can use the &lt;code&gt;timer_start()&lt;/code&gt; function introduced in Vim 8 and neovim to
regularly check for the current Dark Mode settings.  Put this into your Vim&amp;nbsp;config:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; SetBackgroundMode&lt;span class="p"&gt;(&lt;/span&gt;...&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; s:new_bg &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;light&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; $TERM_PROGRAM &lt;span class="p"&gt;==&lt;/span&gt;? &lt;span class="s2"&gt;&amp;quot;Apple_Terminal&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; s:&lt;span class="k"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; systemlist&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;defaults read -g AppleInterfaceStyle&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;[&lt;span class="m"&gt;0&lt;/span&gt;]
        &lt;span class="k"&gt;if&lt;/span&gt; s:&lt;span class="k"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt;? &lt;span class="s2"&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; s:new_bg &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; s:new_bg &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;light&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;endif&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="c"&gt;        &amp;quot; This is for Linux where I use an environment variable for this:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; $VIM_BACKGROUND &lt;span class="p"&gt;==&lt;/span&gt;? &lt;span class="s2"&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; s:new_bg &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;dark&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; s:new_bg &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;light&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;endif&lt;/span&gt;
    &lt;span class="k"&gt;endif&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;background&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt;? s:new_bg
        &lt;span class="k"&gt;let&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;background&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; s:new_bg
    &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;span class="k"&gt;endfunction&lt;/span&gt;
&lt;span class="k"&gt;call&lt;/span&gt; SetBackgroundMode&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;call&lt;/span&gt; timer_start&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;SetBackgroundMode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; {&lt;span class="s2"&gt;&amp;quot;repeat&amp;quot;&lt;/span&gt;: &lt;span class="m"&gt;-1&lt;/span&gt;}&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can &lt;a href="https://appleinsider.com/articles/18/06/14/how-to-toggle-dark-mode-with-a-keyboard-shortcut-or-the-touch-bar"&gt;create an Automator action&lt;/a&gt; that runs the Python script and that
can be activated with a global shortcut.  I use &lt;code&gt;⌥⌘D&lt;/code&gt; (you need to
deactivate this shortcut for showing/hiding the Dock first).  This is the
AppleScript I&amp;nbsp;used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;do shell script&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/usr/local/bin/python3 ~/toggle-macos-dark-mode.py&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;figure&gt;
&lt;img alt="Yo dawg, I heard you like AppleScript … so I wrote some AppleScript that wraps your Python that wraps your AppleScript" src="https://stefan.sofa-rockers.org/images/yo-dawg-applescript.jpg" style="width: 500px; height: 323px" /&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The drawback of this method is that the current application (at the time you
press &lt;code&gt;⌥⌘D&lt;/code&gt;) is used as “source” of the action you get two dialogs asking you
to give that app permissions to remote control the System Settings and&amp;nbsp;Terminal.&lt;/p&gt;
&lt;p&gt;A better solution would be if the authors of NightOwl and Shifty would
integrated this into their tools.  I’m gonna contact them and see what happens.&amp;nbsp;:-)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://macvim-dev.github.io/macvim/"&gt;MacVim&lt;/a&gt; &lt;a href="https://github.com/macvim-dev/macvim/pull/929"&gt;got an&lt;/a&gt; &lt;code&gt;OSAppearanceChanged&lt;/code&gt; event that is emitted every time MacVim changes its&amp;nbsp;appearance.&lt;/p&gt;
&lt;p&gt;Thanks to Frank for the heads&amp;nbsp;up!&lt;/p&gt;
</content><category term="2018"></category><category term="macos"></category><category term="terminal"></category><category term="vim"></category><category term="python"></category><category term="applescript"></category></entry><entry><title>How to mask an image with a smooth circle in PyQt5</title><link href="https://stefan.sofa-rockers.org/2018/05/04/how-to-mask-an-image-with-a-smooth-circle-in-pyqt5/" rel="alternate"></link><published>2018-05-04T13:37:00+02:00</published><updated>2018-05-04T13:37:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2018-05-04:/2018/05/04/how-to-mask-an-image-with-a-smooth-circle-in-pyqt5/</id><summary type="html">&lt;p&gt;Masking a &lt;code&gt;QPixmap&lt;/code&gt; with a smooth circle is not as easily done as
one might wish.  I’ll show you how to do it with the help of
a &lt;code&gt;QImage&lt;/code&gt;, a &lt;code&gt;QPainter&lt;/code&gt; and a &lt;code&gt;QBrush&lt;/code&gt;.  I also take care of
Retina&amp;nbsp;displays.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I am currently working on a &lt;em&gt;PyQt5&lt;/em&gt; based user manager and want to display user
images as&amp;nbsp;circle.&lt;/p&gt;
&lt;p&gt;User image can have any size and aspect ratio, so I also need to crop them
before applying the&amp;nbsp;mask:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Crop image and apply a mask to make it a circle" src="https://stefan.sofa-rockers.org/images/image-mask-circle.png" style="width: 453px; height: 128px" /&gt;
&lt;/figure&gt;
&lt;p&gt;You can easily set a &lt;code&gt;QPixmap&lt;/code&gt; to a &lt;code&gt;QLabel&lt;/code&gt; and show it in your widget.
However, it took me hours to find out how to mask an image with a smooth,
anti-aliased&amp;nbsp;circle.&lt;/p&gt;
&lt;p&gt;If you search for this problem, you’ll find some (relatively old) StackOverflow
questions and topics in Qt5 forums, but none of the proposed solutions worked
for&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;The following snipped&amp;nbsp;does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;crop the original image to a&amp;nbsp;square&lt;/li&gt;
&lt;li&gt;mask it with a smooth, anti-aliased&amp;nbsp;circle&lt;/li&gt;
&lt;li&gt;rescale it to the required&amp;nbsp;size&lt;/li&gt;
&lt;li&gt;take care of Retina / high &lt;span class="caps"&gt;DPI&lt;/span&gt;&amp;nbsp;displays&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PyQt5.QtCore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QRect&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PyQt5.QtGui&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QBrush&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QPainter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QPixmap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QWindow&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PyQt5.QtWidgets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QLabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QVBoxLayout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QWidget&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgdata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return a ``QPixmap`` from *imgdata* masked with a smooth circle.&lt;/span&gt;

&lt;span class="sd"&gt;    *imgdata* are the raw image bytes, *imgtype* denotes the image type.&lt;/span&gt;

&lt;span class="sd"&gt;    The returned image will have a size of *size* × *size* pixels.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="c1"&gt;# Load image and convert to 32-bit ARGB (adds an alpha channel):&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgdata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgtype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convertToFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format_ARGB32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Crop image to a square:&lt;/span&gt;
    &lt;span class="n"&gt;imgsize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the output image with the same dimensions and an alpha channel&lt;/span&gt;
    &lt;span class="c1"&gt;# and make it completely transparent:&lt;/span&gt;
    &lt;span class="n"&gt;out_img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;QImage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format_ARGB32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;out_img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create a texture brush and paint a circle with the original image onto&lt;/span&gt;
    &lt;span class="c1"&gt;# the output image:&lt;/span&gt;
    &lt;span class="n"&gt;brush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QBrush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Create texture brush&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QPainter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Paint the output image&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setBrush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brush&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# Use the image texture brush&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoPen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# Don&amp;#39;t draw an outline&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setRenderHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QPainter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Antialiasing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Use AA&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawEllipse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imgsize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Actually draw the circle&lt;/span&gt;
    &lt;span class="n"&gt;painter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                &lt;span class="c1"&gt;# We are done (segfault if you forget this)&lt;/span&gt;

    &lt;span class="c1"&gt;# Convert the image to a pixmap and rescale it.  Take pixel ratio into&lt;/span&gt;
    &lt;span class="c1"&gt;# account to get a sharp image on retina displays:&lt;/span&gt;
    &lt;span class="n"&gt;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QWindow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QPixmap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setDevicePixelRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;pr&lt;/span&gt;
    &lt;span class="n"&gt;pm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scaled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KeepAspectRatio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SmoothTransformation&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;pm&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QWidget&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Simple window that shows our masked image and text label.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;imgpath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;imgdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rb&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pixmap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mask_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ilabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QLabel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;ilabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setPixmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pixmap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;tlabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QVBoxLayout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ilabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlignCenter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tlabel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Qt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AlignCenter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&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;from&lt;/span&gt; &lt;span class="nn"&gt;PyQt5.QtWidgets&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QApplication&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imgpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exec_&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this is how the result looks&amp;nbsp;like:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="Screenshot of simple PyQt5 app showing our masked image" src="https://stefan.sofa-rockers.org/images/image-mask-circle-screenshot.png" style="width: 228px; height: 262px" /&gt;
&lt;/figure&gt;
&lt;p&gt;I also toyed around with &lt;code&gt;QPainter.setCompositionMode()&lt;/code&gt; and
&lt;code&gt;QPixmap.setMask()&lt;/code&gt; but both approaches did not lead to the desired&amp;nbsp;results.&lt;/p&gt;
&lt;p&gt;If you have found a better solution for this problem, please let me know via
&lt;a href="https://octodon.social/&amp;#64;sscherfke"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://twitter.com/sscherfke"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content><category term="2018"></category><category term="python"></category></entry><entry><title>Assertions and Exceptions</title><link href="https://stefan.sofa-rockers.org/2018/04/16/assertions-and-exceptions/" rel="alternate"></link><published>2018-04-16T09:26:00+02:00</published><updated>2018-04-16T09:26:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2018-04-16:/2018/04/16/assertions-and-exceptions/</id><summary type="html">&lt;p&gt;This article explains the difference between assertions and
exceptions and when to use which&amp;nbsp;one.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I recently had a discussion about the difference of Python’s &lt;code&gt;assert&lt;/code&gt; keyword
vs. raising “normal” exceptions.  For quite a long time, I was uncertain when
to use &lt;code&gt;assert&lt;/code&gt;, too.  In this article, I’ll present some examples and rules
of thumb for when using &lt;code&gt;assert&lt;/code&gt; or raising exceptions is – in my opinion
– more&amp;nbsp;appropriate.&lt;/p&gt;
&lt;section id="when-do-i-use-assert"&gt;
&lt;h2&gt;When do I use &lt;code&gt;assert&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;You use &lt;code&gt;assert&lt;/code&gt; to check and document invariants of your code.  Invariants
are conditions in the middle of your code that should always be true (For
example: “At this point in time &lt;code&gt;x&lt;/code&gt; should always be a number between 0 and&amp;nbsp;10”).&lt;/p&gt;
&lt;p&gt;Assertions can help others (including future-you) to understand your code and
they should make your program crash if the assertion becomes false (because of
a programming&amp;nbsp;error).&lt;/p&gt;
&lt;p&gt;Here are some examples for when &lt;code&gt;assert&lt;/code&gt; is&amp;nbsp;useful:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Bad:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# Some operations on &amp;quot;x&amp;quot;&lt;/span&gt;
    &lt;span class="c1"&gt;# x should still contain 2 elements:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;


&lt;span class="c1"&gt;# Good:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# Some operations on &amp;quot;x&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;len(x) == &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# We have widget like this:&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#   Step size: ( ) auto   (•) manual: [___300] seconds&lt;/span&gt;

&lt;span class="c1"&gt;# Bad:&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button_auto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isChecked&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&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;# The user provided a manual step size:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step_size_input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="c1"&gt;# Good:&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button_auto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isChecked&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button_manual&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isChecked&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step_size_input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Bad:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fill_list&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="c1"&gt;# self.dest should be empty, but self.source not:&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source_not_empty&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take_from_source&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="c1"&gt;# Good:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fill_list&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;assert&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;assert&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source_not_empty&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take_from_source&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;assert&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="validation-of-input-parameters"&gt;
&lt;h2&gt;Validation of input&amp;nbsp;parameters&lt;/h2&gt;
&lt;p&gt;Sometimes, &lt;code&gt;assert&lt;/code&gt; is used to validate input parameters for functions.  You
should avoid this because &lt;code&gt;assert&lt;/code&gt; statements are not executed when Python is
invoked with &lt;code&gt;-O[O]&lt;/code&gt;.  In this case, you would suddenly loose all input&amp;nbsp;validation.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AssertionErrors&lt;/code&gt; also carry less semantics than “normal” exceptions – like
&lt;code&gt;TypeError&lt;/code&gt; when the user passed a string and not a number, or &lt;code&gt;ValueError&lt;/code&gt;
when the user passed a negative number when only positive numbers are&amp;nbsp;allowed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Bad:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a must be an int&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a must be &amp;gt;= 0&amp;#39;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Good:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a must be an int but is a &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&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;a&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a must be &amp;gt;= 0 but is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are, however, exceptions possible for this rule.  Imagine the following
snippet where we want to add/delete a user from a group on an &lt;span class="caps"&gt;LDAP&lt;/span&gt;&amp;nbsp;server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_to_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;_modify_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODIFY_ADD&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;delete_from_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_modify_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODIFY_DELETE&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;_modify_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ldap3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODIFY_ADD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ldap3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MODIFY_DELETE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;uniqueMember&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;])]})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this case, it’s appropriate to use &lt;code&gt;assert&lt;/code&gt; because &lt;code&gt;_modify_group()&lt;/code&gt;
should never be called by our users but only by &lt;code&gt;add_to_group()&lt;/code&gt; and
&lt;code&gt;delete_from_group()&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-kind-of-exceptions-should-i-raise"&gt;
&lt;h2&gt;What kind of exceptions should I&amp;nbsp;raise?&lt;/h2&gt;
&lt;p&gt;Within you own code, the built-in exception types – especially &lt;code&gt;ValueError&lt;/code&gt;,
&lt;code&gt;AttributeError&lt;/code&gt;, &lt;code&gt;TypeError&lt;/code&gt; and &lt;code&gt;RuntimeError&lt;/code&gt; – are usually&amp;nbsp;sufficient.&lt;/p&gt;
&lt;p&gt;Libraries often define their own exception hierarchy with their own base class.
For example, &lt;a href="http://docs.python-requests.org/en/master/"&gt;Requests&lt;/a&gt; has
&lt;code&gt;RequestError&lt;/code&gt; from which all other exception types&amp;nbsp;inherit.&lt;/p&gt;
&lt;p&gt;This makes it a lot easier for users to catch your library’s exceptions.  If
you raise all kinds of exceptions (and then you don’t document that, too), your
users will end up using a broad/bare excepts  which is&amp;nbsp;bad.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;except [[Base]Exception]&lt;/code&gt; is bad because it results in all kinds of nasty
side effects.  For example, it would also catch (and possibly ignore)
&lt;code&gt;KeyboardInterrupt&lt;/code&gt; (&lt;code&gt;Ctrl+C&lt;/code&gt;) or &lt;code&gt;AsstionError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And that’s why you should also never raise &lt;code&gt;[Base]Exception&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="tldr"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;TLDR&lt;/span&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;assert&lt;/code&gt; to detect programming errors and conditions that should never
occur and which should crash your program immediately, e.g., invariants of an&amp;nbsp;algorithm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;  Assertion checks are stripped and &lt;em&gt;not&lt;/em&gt; executed if Python is
invoked with the &lt;code&gt;-O&lt;/code&gt; or &lt;code&gt;--OO&lt;/code&gt; option!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Raise an exception for errors that are caused by invalid user input or other
problems with the environment (e.g., network errors or unreadable&amp;nbsp;files).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Never raise or catch &lt;code&gt;[Base]Exception&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content><category term="2018"></category><category term="python"></category></entry><entry><title>Getting started with devpi</title><link href="https://stefan.sofa-rockers.org/2017/11/09/getting-started-with-devpi/" rel="alternate"></link><published>2017-11-09T21:33:00+01:00</published><updated>2017-11-09T21:33:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2017-11-09:/2017/11/09/getting-started-with-devpi/</id><summary type="html">&lt;p&gt;How to get started with devpi and evaluate it for hosting a company’s&amp;nbsp;packages.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Recently, I wanted to (re)evaluate &lt;a href="https://docs.devpi.net"&gt;devpi&lt;/a&gt; for use in our company.  I have
already worked with it some years ago but–by now–forgot what exactly it can
do and how to set it&amp;nbsp;up.&lt;/p&gt;
&lt;p&gt;However, the marketing on the &lt;a href="https://devpi.net/docs/devpi/devpi/stable/"&gt;landing page of the docs&lt;/a&gt; and &lt;a href="https://github.com/devpi/devpi"&gt;on GitHub&lt;/a&gt; was
not very convincing and I nearly ended up working with another&amp;nbsp;product.&lt;/p&gt;
&lt;p&gt;A &lt;a href="https://twitter.com/sscherfke/status/922493403258130433"&gt;short Twitter discussion later&lt;/a&gt;, I decided to give devpi a try and write
down my findings.  Maybe they can contribute to improving devpi’s docs and
demonstrate how easy it is to&amp;nbsp;use.&lt;/p&gt;
&lt;p&gt;This is what I was trying to achieve with&amp;nbsp;devpi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mirror and cache&amp;nbsp;PyPI&lt;/li&gt;
&lt;li&gt;Extend PyPI with an index for internal stable&amp;nbsp;packages&lt;/li&gt;
&lt;li&gt;Extend the stable index with a staging index for experimental new&amp;nbsp;features&lt;/li&gt;
&lt;li&gt;No free registration for&amp;nbsp;users&lt;/li&gt;
&lt;li&gt;Only a single, authorized user (our &lt;a href="https://docs.gitlab.com/ce/ci/README.html"&gt;GitLab &lt;span class="caps"&gt;CI&lt;/span&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Use standard tools (&lt;a href="https://pip.pypa.io/en/stable/"&gt;pip&lt;/a&gt;, &lt;a href="https://github.com/pypa/twine"&gt;twine&lt;/a&gt;, …) as much as&amp;nbsp;possible&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="installation"&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;Devpi consists of a server, a command line client and a web front-end.  The
meta package &lt;code&gt;devpi&lt;/code&gt; installs them&amp;nbsp;all:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;mkvirtualenv&lt;span class="w"&gt; &lt;/span&gt;devpi
&lt;span class="gp"&gt;$ &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;devpi
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;Installing collected packages: ...&lt;/span&gt;
&lt;span class="go"&gt;Successfully installed ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="setting-up-the-server"&gt;
&lt;h2&gt;Setting up the&amp;nbsp;server&lt;/h2&gt;
&lt;p&gt;Devpi uses SQLite to store its data, so we don’t need to setup a database (you
&lt;a href="https://pypi.python.org/pypi/devpi-postgresql"&gt;can use PostgreSQL&lt;/a&gt; though, if you&amp;nbsp;want).&lt;/p&gt;
&lt;p&gt;When you start the &lt;code&gt;devpi-server&lt;/code&gt; for the first time, you need to pass the
&lt;code&gt;--init&lt;/code&gt; option.  You an also specify where it should store its data (the
default is &lt;code&gt;~/.devpi&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;devpi-server&lt;span class="w"&gt; &lt;/span&gt;--serverdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/devpi&lt;span class="w"&gt; &lt;/span&gt;--init
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you don’t use the standard location, you must set the &lt;code&gt;--serverdir&lt;/code&gt; option
every time you start the&amp;nbsp;server.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="user-management"&gt;
&lt;h2&gt;User&amp;nbsp;management&lt;/h2&gt;
&lt;p&gt;We can now set a password for the root user and allow only root to create new&amp;nbsp;users:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;devpi-server&lt;span class="w"&gt; &lt;/span&gt;--serverdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/devpi&lt;span class="w"&gt; &lt;/span&gt;--passwd&lt;span class="w"&gt; &lt;/span&gt;root
&lt;span class="go"&gt;enter password for root:&lt;/span&gt;
&lt;span class="go"&gt;repeat password for root:&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;devpi-server&lt;span class="w"&gt; &lt;/span&gt;--serverdir&lt;span class="o"&gt;=&lt;/span&gt;/tmp/devpi&lt;span class="w"&gt; &lt;/span&gt;--restrict-modify&lt;span class="o"&gt;=&lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;--start
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;starting background devpi-server at http://localhost:3141&lt;/span&gt;
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Like the &lt;code&gt;--serverdir&lt;/code&gt; option, you must always pass
&lt;code&gt;--restrict-modify=root&lt;/code&gt; when you start the&amp;nbsp;server.&lt;/p&gt;
&lt;p&gt;Once the server is running, we can use the devpi client &lt;code&gt;devpi&lt;/code&gt; to create an
additional user named &lt;em&gt;packages&lt;/em&gt;.  Prior to that, tell the devpi client on
which server we want to operate with the following&amp;nbsp;commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;http://localhost:3141
&lt;span class="go"&gt;...&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;login&lt;span class="w"&gt; &lt;/span&gt;root
&lt;span class="go"&gt;password for user root:&lt;/span&gt;
&lt;span class="go"&gt;logged in &amp;#39;root&amp;#39;, credentials valid for 10.00 hours&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;packaging@company.com&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;packages
&lt;span class="go"&gt;user created: packages&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;span class="go"&gt;packages&lt;/span&gt;
&lt;span class="go"&gt;root&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="package-indexes"&gt;
&lt;h2&gt;Package&amp;nbsp;indexes&lt;/h2&gt;
&lt;p&gt;By default, devpi creates an index called &lt;em&gt;root/pypi&lt;/em&gt;.  It serves as a proxy
and cache for PyPI and you can’t upload your own packages to&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;However, devpi supports index inheritance: We can create our &lt;em&gt;stable&lt;/em&gt; index in
the &lt;em&gt;packages&lt;/em&gt; namespace and set &lt;em&gt;root/pypi&lt;/em&gt; as base.  If we query
&lt;em&gt;packages/stable&lt;/em&gt;, devpi first searches this index and than falls back to
&lt;em&gt;root/pypi&lt;/em&gt; if it can’t find the package on the first&amp;nbsp;index.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;index&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;packages/stable&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root/pypi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;volatile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;False
&lt;span class="go"&gt;http://localhost:3141/packages/stable:&lt;/span&gt;
&lt;span class="go"&gt;  type=stage&lt;/span&gt;
&lt;span class="go"&gt;  bases=root/pypi&lt;/span&gt;
&lt;span class="go"&gt;  volatile=False&lt;/span&gt;
&lt;span class="go"&gt;  acl_upload=packages&lt;/span&gt;
&lt;span class="go"&gt;  mirror_whitelist=&lt;/span&gt;
&lt;span class="go"&gt;  pypi_whitelist=&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Similarly, our &lt;em&gt;staging&lt;/em&gt; index can inherit &lt;em&gt;stable&lt;/em&gt;.  Devpi will than search
&lt;em&gt;packages/staging&lt;/em&gt;, &lt;em&gt;packages/stable&lt;/em&gt; and finally &lt;em&gt;root/pypi&lt;/em&gt; for&amp;nbsp;packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;devpi&lt;span class="w"&gt; &lt;/span&gt;index&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;company/staging&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;company/stable&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;volatile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True
&lt;span class="go"&gt;http://localhost:3141/company/staging:&lt;/span&gt;
&lt;span class="go"&gt;  type=stage&lt;/span&gt;
&lt;span class="go"&gt;  bases=company/stable&lt;/span&gt;
&lt;span class="go"&gt;  volatile=True&lt;/span&gt;
&lt;span class="go"&gt;  acl_upload=company&lt;/span&gt;
&lt;span class="go"&gt;  mirror_whitelist=&lt;/span&gt;
&lt;span class="go"&gt;  pypi_whitelist=&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;volatile=True&lt;/code&gt; option lets us perform destructive actions on the index
(like overriding or deleting&amp;nbsp;packages).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="install-packages"&gt;
&lt;h2&gt;Install&amp;nbsp;packages&lt;/h2&gt;
&lt;p&gt;Let’s use our devpi to load a public package from&amp;nbsp;PyPI:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;http://localhost:3141/company/stable&lt;span class="w"&gt; &lt;/span&gt;click
&lt;span class="go"&gt;Collecting click&lt;/span&gt;
&lt;span class="go"&gt;  Downloading http://localhost:3141/root/pypi/+f/5e7/a4e296b3212da/click-6.7-py2.py3-none-any.whl (71kB)&lt;/span&gt;
&lt;span class="go"&gt;Installing collected packages: click&lt;/span&gt;
&lt;span class="go"&gt;Successfully installed click-6.7&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can also &lt;a href="http://pip.readthedocs.io/en/stable/user_guide/#config-file"&gt;configure pip&lt;/a&gt; to use our devpi as default&amp;nbsp;index:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[global]&lt;/span&gt;
&lt;span class="na"&gt;index-url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://localhost:3141/company/stable&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="upload-packages"&gt;
&lt;h2&gt;Upload&amp;nbsp;packages&lt;/h2&gt;
&lt;p&gt;You can build and upload packages with your &lt;a href="https://hynek.me/articles/sharing-your-labor-of-love-pypi-quick-and-dirty/"&gt;usual workflow&lt;/a&gt;.  Just add devpi
to your  &lt;code&gt;~/.pypirc&lt;/code&gt; (&lt;a href="https://glyph.twistedmatrix.com/2017/10/careful-with-that-pypi.html"&gt;Do not store passwords in there!&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[distutils]&lt;/span&gt;
&lt;span class="na"&gt;index-servers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;devpi-stable&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;devpi-staging&lt;/span&gt;

&lt;span class="k"&gt;[devpi-stable]&lt;/span&gt;
&lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://localhost:3141/packages/stable/&lt;/span&gt;
&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;

&lt;span class="k"&gt;[devpi-staging]&lt;/span&gt;
&lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://localhost:3141/packages/staging/&lt;/span&gt;
&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;packages&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we can build some&amp;nbsp;dists:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/Projects/simpy
&lt;span class="gp"&gt;$ &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;sdist&lt;span class="w"&gt; &lt;/span&gt;bdist_wheel
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And upload&amp;nbsp;them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;twine
&lt;span class="go"&gt;...&lt;/span&gt;
&lt;span class="go"&gt;Installing collected packages: ...&lt;/span&gt;
&lt;span class="go"&gt;Successfully installed ...&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;twine&lt;span class="w"&gt; &lt;/span&gt;upload&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;devpi-stable&lt;span class="w"&gt; &lt;/span&gt;dist/*
&lt;span class="go"&gt;Uploading distributions to http://localhost:3141/packages/stable/&lt;/span&gt;
&lt;span class="go"&gt;Enter your password:&lt;/span&gt;
&lt;span class="go"&gt;Uploading simpy-3.0.10-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class="go"&gt;Uploading simpy-3.0.10.tar.gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="congratulation"&gt;
&lt;h2&gt;Congratulation!&lt;/h2&gt;
&lt;p&gt;You can now install your own packages from your own packages&amp;nbsp;index:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;simpy
&lt;span class="go"&gt;Collecting simpy&lt;/span&gt;
&lt;span class="go"&gt;  Downloading simpy-3.0.10-py2.py3-none-any.whl&lt;/span&gt;
&lt;span class="go"&gt;Installing collected packages: simpy&lt;/span&gt;
&lt;span class="go"&gt;Successfully installed simpy-3.0.10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Devpi’s docs may appear a bit confusing and look a little demure, but devpi
itself is actually really easy to setup and use – and powerful at the same&amp;nbsp;time!&lt;/p&gt;
&lt;/section&gt;
</content><category term="2017"></category><category term="python"></category><category term="pypi"></category><category term="devpi"></category><category term="packaging"></category></entry><entry><title>Advanced asyncio testing</title><link href="https://stefan.sofa-rockers.org/2016/03/10/advanced-asyncio-testing/" rel="alternate"></link><published>2016-03-10T16:22:00+01:00</published><updated>2016-03-10T16:22:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2016-03-10:/2016/03/10/advanced-asyncio-testing/</id><summary type="html">&lt;p&gt;I demonstrate how you can use pytest’s fixture system and the plug-in
&lt;em&gt;pytest-asyncio&lt;/em&gt; to reduce the boilerplate code required for testing
networking&amp;nbsp;libraries.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In my &lt;a href="https://stefan.sofa-rockers.org/2015/04/22/testing-coroutines/"&gt;last article&lt;/a&gt;, I showed how pytest’s fixture system and plug-in
infrastructure can help you with writing cleaner and better tests.  Fixtures
allow you to create a clean event loop instance for every test case.  The
plug-in system allows you to write test functions that are actually asyncio
coroutines.  While I was working on that articel, &lt;em&gt;Tin Tvrtkovic&lt;/em&gt; created the
plug-in &lt;a href="https://pypi.python.org/pypi/pytest-asyncio"&gt;pytest-asyncio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, it lets you do&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_coro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_coro&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_event_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So using &lt;em&gt;pytest-asyncio&lt;/em&gt; clearly improves your test (and there is even more,
what this plug-in&amp;nbsp;does!).&lt;/p&gt;
&lt;p&gt;While I have been working on &lt;a href="https://aiomas.readthedocs.org"&gt;aiomas&lt;/a&gt;, some additional requirements came up
that were not so easily covered.  What &lt;em&gt;aiomas&lt;/em&gt; basically does is adding three
layers of abstraction around the asyncio&amp;nbsp;transports:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;em&gt;channel&lt;/em&gt; layer lets you send &lt;span class="caps"&gt;JSON&lt;/span&gt; or MsgPack encoded messages in
a request-reply manner.  This layer uses a custom protocol that works with
different kinds of transports: &lt;span class="caps"&gt;TCP&lt;/span&gt; sockets, Unix domain sockets and custom
transport called &lt;em&gt;local queue&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;&lt;span class="caps"&gt;RPC&lt;/span&gt;&lt;/em&gt; layer creates a remote-procedure-call system on top of the
&lt;em&gt;channel&lt;/em&gt;&amp;nbsp;layer.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;agent&lt;/em&gt; layer (for multi-agent systems) hides even more of the
networking-related stuff and lets you basically write classes that call
methods of other classes over a network&amp;nbsp;connection.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is a simple example of how the &lt;em&gt;channel&lt;/em&gt; layer&amp;nbsp;works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiomas&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Handle a client connection.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cya&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Client coroutine: Send a greeting to the server and wait for a&lt;/span&gt;
&lt;span class="sd"&gt;    reply.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_connection&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5555&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5555&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_closed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;section id="requirements-for-our-tests"&gt;
&lt;h2&gt;Requirements for our&amp;nbsp;tests&lt;/h2&gt;
&lt;p&gt;So with this in mind, I had the following requirements for my&amp;nbsp;tests:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I need a clean event loop instance for every&amp;nbsp;test.&lt;/p&gt;
&lt;p&gt;This can be solved with the &lt;code&gt;event_loop&lt;/code&gt; fixture provided by
&lt;em&gt;pytest-asyncio&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every test should be run with every transport available (&lt;span class="caps"&gt;TCP&lt;/span&gt; socket, Unix
domain socket,&amp;nbsp;…).&lt;/p&gt;
&lt;p&gt;This could in theory be solved with the &lt;code&gt;pytest.mark.parametrize()&lt;/code&gt;
decorator (but not in my case as we will see&amp;nbsp;later).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every test needs a client coroutine.  Ideally, this would be the test&amp;nbsp;itself.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;pytest-asyncio’s&lt;/em&gt; &lt;code&gt;pytest.mark.asyncio&lt;/code&gt; decorator solves&amp;nbsp;this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every test needs a server with a custom callback for client connections.
Servers must be cleanly shut down no matter what the outcome of the test&amp;nbsp;is.&lt;/p&gt;
&lt;p&gt;It would seem that a fixture would do the job, but every server needs a test
specific callback for handling client connections.  This makes it a lot&amp;nbsp;harder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I don’t want any “address already in use” errors if one test fails&amp;nbsp;badly.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;pytest-asyncio’s&lt;/em&gt; &lt;code&gt;unused_tcp_port&lt;/code&gt; fixture comes to&amp;nbsp;help.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I don’t want to use &lt;code&gt;loop.run_until_complete()&lt;/code&gt; all the&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;Again, the &lt;code&gt;pytest.mark.asyncio&lt;/code&gt; decorator solves&amp;nbsp;this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To wrap up what remains to be solved:  Every test needs at least two fixtures
(one for the event loop, one for the address type), but I want to combine them
as a single fixture.  Creating a fixture for setting up a server would also be
nice, but how can we do&amp;nbsp;this?&lt;/p&gt;
&lt;/section&gt;
&lt;section id="our-first-approach"&gt;
&lt;h2&gt;Our first&amp;nbsp;approach&lt;/h2&gt;
&lt;p&gt;The first thing we can do is to wrap the loop and the address type in
a fixture.  We’ll call it &lt;em&gt;ctx&lt;/em&gt; (short for &lt;em&gt;test context&lt;/em&gt;).  With fixture
parameters, it is also easy to create one fixture instance for every address&amp;nbsp;type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;


&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&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;ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unused_tcp_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;short_tmpdir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate tests with TCP sockets and Unix domain sockets.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unused_tcp_port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;short_tmpdir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strpath&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Unknown addr type: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&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;ctx&lt;/span&gt;


&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yield_fixture&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;short_tmpdir&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate a short temp. dir for Unix domain sockets.  The paths&lt;/span&gt;
&lt;span class="sd"&gt;    provided by ptest&amp;#39;s tmpdir fixture are too long on some platforms.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TemporaryDirectory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tdir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tdir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This lets us write our tests like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiomas&lt;/span&gt;

&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cya&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_closed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;cya&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This works already very nicely and every test using the &lt;code&gt;ctx&lt;/code&gt; fixture is run
once for every address&amp;nbsp;type.&lt;/p&gt;
&lt;p&gt;However, two problems&amp;nbsp;remain:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our &lt;code&gt;ctx&lt;/code&gt; fixture always requires an unused &lt;span class="caps"&gt;TCP&lt;/span&gt; port &lt;em&gt;and&lt;/em&gt; a temporary
directory – although we only need one of both in each&amp;nbsp;case.&lt;/li&gt;
&lt;li&gt;Setting up the server (and closing it) also involves some code which will be
the same for every test and should thus be moved into a fixture.  However,
a &lt;code&gt;server&lt;/code&gt; fixture won’t work directly, because every server needs a test
specific callback as you can see in the line where we create the server
(&lt;code&gt;server = await ...&lt;/code&gt;).  But without a &lt;code&gt;server&lt;/code&gt; fixture, we can’t have
a tear-down method for it&amp;nbsp;…&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s see how we can tackle these&amp;nbsp;issues.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="approach-number-two"&gt;
&lt;h2&gt;Approach number&amp;nbsp;two&lt;/h2&gt;
&lt;p&gt;The first problem can be solved by using the &lt;code&gt;getfuncargvalue()&lt;/code&gt; method of
the &lt;em&gt;request&lt;/em&gt; object that our fixture receives.  Using this method, we can
manually call a fixture&amp;nbsp;function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&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;ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate tests with TCP sockets and Unix domain sockets.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getfuncargvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unused_tcp_port&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tmpdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getfuncargvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;short_tmpdir&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmpdir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strpath&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Unknown addr type: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&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;ctx&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To help with issue number two, we can extend our &lt;code&gt;Context&lt;/code&gt; class that is
passed into every test.  We add a method
&lt;code&gt;Context.start_server(client_handler)&lt;/code&gt; that we can call from within our
tests.  We also add a finalize/teardown part to our &lt;code&gt;ctx&lt;/code&gt; fixture that will
close the server once we are done.  And while we are at it, we’ll also create
some more shortcut&amp;nbsp;functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create and return a connection to &amp;quot;self.addr&amp;quot;.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_server&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;handle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Start a server with the callback *handle_client* listening on&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;self.addr&amp;quot;.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_server_and_connect&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;handle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="n"&gt;server_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                       &lt;span class="n"&gt;client_kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Shortcut for::&lt;/span&gt;

&lt;span class="sd"&gt;            await ctx.start_server(...)&lt;/span&gt;
&lt;span class="sd"&gt;            channel = await ctx.connect()&amp;quot;&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;server_kwargs&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server_kwargs&lt;/span&gt; &lt;span class="o"&gt;=&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;client_kwargs&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;client_kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;server_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;client_kwargs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close_server&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Close the server.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait_closed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yield_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&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;ctx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Generate tests with TCP sockets and Unix domain sockets.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tcp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getfuncargvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unused_tcp_port&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;unix&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;tmpdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getfuncargvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;short_tmpdir&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmpdir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sock&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strpath&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Unknown addr type: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;addr_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;

    &lt;span class="c1"&gt;# Shutdown the server and wait for all pending tasks to finish:&lt;/span&gt;
    &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close_server&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_loop&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                              &lt;span class="n"&gt;return_exceptions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this extra functionality, our test case becomes a lot shorter, easier to
read, and more&amp;nbsp;reliable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiomas&lt;/span&gt;

&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cya&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


    &lt;span class="n"&gt;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_server_and_connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;cya&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;ctx&lt;/code&gt; fixture (and the associated &lt;code&gt;Context&lt;/code&gt; class) is indeed not the
shortest fixture I ever wrote, but it helped me to remove approx. 200 lines of
boilerplate code from my tests (apart from making them more readable and&amp;nbsp;maintainable).&lt;/p&gt;
&lt;/section&gt;
</content><category term="2016"></category><category term="python"></category><category term="testing"></category><category term="pytest"></category><category term="asyncio"></category><category term="aiomas"></category></entry><entry><title>Testing (asyncio) coroutines with pytest</title><link href="https://stefan.sofa-rockers.org/2015/04/22/testing-coroutines/" rel="alternate"></link><published>2015-04-22T20:34:00+02:00</published><updated>2015-04-22T20:34:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2015-04-22:/2015/04/22/testing-coroutines/</id><summary type="html">&lt;p&gt;Pytest currently offers no helpers for testing (asyncio) coroutines.
Its powerful plug-in architecture allows you fix that and write
elegant tests for&amp;nbsp;coroutines.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://pytest.org"&gt;Pytest&lt;/a&gt; is an awesome testing package for Python and
since long one of my favorite Python packages in general.  It makes writing
test &lt;a href="https://pytest.org/latest/getting-started.html#getstarted"&gt;really easy&lt;/a&gt;
and its reporting capabilities for test failures are extremely&amp;nbsp;helpful.&lt;/p&gt;
&lt;p&gt;However, it currently (as of version 2.7) doesn’t help you very much with
testing (asyncio) coroutines.  So a naïve approach for testing coroutines would&amp;nbsp;be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# tests/test_coros.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_coro&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nd"&gt;@asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coroutine&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# onoes!&lt;/span&gt;

    &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_test&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This approach has several problems and a lot of overhead.  The only interesting
lines are the ones containing the &lt;code&gt;yield from&lt;/code&gt; and &lt;code&gt;assert&lt;/code&gt; statements.&lt;/p&gt;
&lt;p&gt;It would be better if every test case had its own event loop instance that gets
correctly closed no matter if the test passes or&amp;nbsp;fails.&lt;/p&gt;
&lt;p&gt;We also cannot simply &lt;code&gt;yield&lt;/code&gt; within our test case because pytest would then
think that our test case yields new test cases which is not the case here.  So
we have to create a separate coroutine which contains our actual test and fire
up the even loop in order to execute&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;Our tests would look a lot cleaner and behave better if we could instead do
something like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# tests/test_coros.py&lt;/span&gt;

&lt;span class="nd"&gt;@asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coroutine&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_coro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It turns out that thanks to pytests flexible plug-in system, it’s possible
to implement the desired behavior.  Although,  most of the required hooks are not
documented very well or at all which also makes it relatively hard to find out
which hooks you have to implement and&amp;nbsp;how.&lt;/p&gt;
&lt;p&gt;We create a &lt;a href="https://pytest.org/latest/plugins.html#conftest-py-local-per-directory-plugins"&gt;local per-directory plug-in&lt;/a&gt;
since this is a little bit easier than creating a “real”, external plug-in.
Pytest looks in every test-directory for a file called &lt;code&gt;conftest.py&lt;/code&gt; and
applies the fixtures and hooks implemented there to all tests within that&amp;nbsp;directory.&lt;/p&gt;
&lt;p&gt;So lets start by writing a fixture that creates a new event loop instance for
each test case and properly close it when the test is&amp;nbsp;done:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# tests/conftest.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;


&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yield_fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Set-up&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_event_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;

    &lt;span class="c1"&gt;# Clean-up&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="c1"&gt;# tests/test_coros.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_coro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coroutine&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# onoes!&lt;/span&gt;

    &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;do_test&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before each test, &lt;a href="https://pytest.org/latest/yieldfixture.html"&gt;pytest executes&lt;/a&gt; the &lt;code&gt;loop&lt;/code&gt; fixture until the
first &lt;code&gt;yield&lt;/code&gt; statement.  What gets yielded is then passed to the &lt;em&gt;loop&lt;/em&gt;
argument of our test case.  When the test ends (successfully or not), pytest
finishes the execution of the &lt;code&gt;loop&lt;/code&gt; fixture and thus closes the loop
properly.  In the same way, you could write a fixture that creates a socket and
closes it after each test (your socket fixture can depend on the loop fixture
in the same way as our test does.  Nice, isn’t&amp;nbsp;it?).&lt;/p&gt;
&lt;p&gt;But we are still not done yet.  Let’s teach pytest how to execute our test
coroutines.  Therefore, we need to change how asyncio coroutines are collected
(they should be collected like normal test functions, not like test generators)
and how they get executed (via &lt;code&gt;loop.run_until_complete()&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# tests/conftest.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_pycollect_makeitem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collector&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="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Collect asyncio coroutines as normal functions, not as generators.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;funcnamefilter&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="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iscoroutinefunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# We return a list of test function objects.  Depending on the&lt;/span&gt;
        &lt;span class="c1"&gt;# fixtures, one test function can result in multiple test items&lt;/span&gt;
        &lt;span class="c1"&gt;# (i.e., when it is decorated with &amp;quot;pytest.mark.parametrize()&amp;quot;.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_genfunctions&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="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# else:&lt;/span&gt;
    &lt;span class="c1"&gt;#     We return None and pytest&amp;#39;s default behavior gets applied to &amp;quot;obj&amp;quot;&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_pyfunc_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pyfuncitem&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;If ``pyfuncitem.obj`` is an asyncio coroutinefunction, execute it via&lt;/span&gt;
&lt;span class="sd"&gt;    the event loop instead of calling it directly.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;testfunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyfuncitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iscoroutinefunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testfunction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Return None if its not a coroutine.  Pytest will handle the&lt;/span&gt;
        &lt;span class="c1"&gt;# test item in the normal way.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract required arguments from all available fixtures:&lt;/span&gt;
    &lt;span class="n"&gt;funcargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyfuncitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;funcargs&lt;/span&gt;  &lt;span class="c1"&gt;# dict with all fixtures&lt;/span&gt;
    &lt;span class="n"&gt;argnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pyfuncitem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fixtureinfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argnames&lt;/span&gt;  &lt;span class="c1"&gt;# Args for this test&lt;/span&gt;
    &lt;span class="n"&gt;testargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;funcargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;argnames&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the generator object for this test (test will not yet execute!)&lt;/span&gt;
    &lt;span class="n"&gt;coro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testfunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;testargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Run the coro in the event loop and execute the test&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;loop&amp;#39;&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;loop&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;testargs&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# Signal pytest that we executed the test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This plug-in should work with pytest 2.4 or newer.  I tested it with versions
2.6 and&amp;nbsp;2.7.&lt;/p&gt;
&lt;p&gt;It seems like the pytest devs are currently trying to clean-up and refactor
pytest’s plug-in system.  Maybe, a future release will even contain similar
functionality to our plug-in.  It would be really nice if pytest supported
testing coroutines out-of-the-box as they are becoming increasingly popular
amongst various&amp;nbsp;frameworks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; There’s also &lt;a href="https://github.com/Tinche/pytest-asyncio"&gt;pytest-asyncio&lt;/a&gt; .  It was created after I posted
the solution described above &lt;a href="http://stackoverflow.com/questions/28283472/py-test-mixing-fixtures-and-asyncio-coroutines"&gt;to Stack Overflow&lt;/a&gt;.&lt;/p&gt;
&lt;small&gt;Thanks to &lt;em&gt;ronny&lt;/em&gt; and &lt;em&gt;hpk&lt;/em&gt; for proof-reading this
article.&lt;/small&gt;</content><category term="2015"></category><category term="python"></category><category term="testing"></category><category term="pytest"></category><category term="asyncio"></category></entry><entry><title>aiomas – A library for multi-agent systems and RPC based on asyncio</title><link href="https://stefan.sofa-rockers.org/2015/02/13/aiomas/" rel="alternate"></link><published>2015-02-13T10:40:00+01:00</published><updated>2015-02-13T10:40:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2015-02-13:/2015/02/13/aiomas/</id><summary type="html">&lt;p&gt;&lt;em&gt;aiomas&lt;/em&gt; is a new, &lt;em&gt;asyncio&lt;/em&gt; based library that helps you with
creating distributed multi-agent systems.  It allows different clocks
(real-time, coupled to simulations, …) and different codecs (MsgPack,
&lt;span class="caps"&gt;JSON&lt;/span&gt;) to be used.  You can also just use the lower level abstractions
for &lt;span class="caps"&gt;RPC&lt;/span&gt; or message based request-reply&amp;nbsp;channels.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/aiomas"&gt;aiomas&lt;/a&gt; is a new, &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio&lt;/a&gt; based library that helps you with creating
distributed multi-agent systems.  It allows different clocks (real-time,
coupled to simulations, …) and different codecs (MsgPack, &lt;span class="caps"&gt;JSON&lt;/span&gt;) to be used.
You can also just use the lower level abstractions for &lt;span class="caps"&gt;RPC&lt;/span&gt; or message based
request-reply&amp;nbsp;channels.&lt;/p&gt;
&lt;p&gt;I started the development in December 2014 and &lt;a href="http://www.offis.de/en/"&gt;&lt;span class="caps"&gt;OFFIS&lt;/span&gt;&lt;/a&gt;, the institute I’m
working at, now kindly allowed me to release it as &lt;span class="caps"&gt;OSS&lt;/span&gt; (licensed under the &lt;span class="caps"&gt;MIT&lt;/span&gt;&amp;nbsp;license).&lt;/p&gt;
&lt;section id="what-but-why"&gt;
&lt;h2&gt;What? But&amp;nbsp;why?&lt;/h2&gt;
&lt;p&gt;Multi-agent systems are a &lt;em&gt;thing&lt;/em&gt; in many scientific institutes, including
&lt;span class="caps"&gt;OFFIS&lt;/span&gt;.  In particular, my colleagues and I want to create a multi-agent system
for managing dynamic &lt;a href="https://en.wikipedia.org/wiki/Virtual_power_plant"&gt;virtual power plants&lt;/a&gt; in our current project &lt;a href="https://dynamicvpp.offis.de"&gt;Dynamic
&lt;span class="caps"&gt;VPP&lt;/span&gt;&lt;/a&gt; (German&amp;nbsp;link).&lt;/p&gt;
&lt;p&gt;A very common tool (at least in science) for writing multi-agent systems is
&lt;a href="http://jade.tilab.com/"&gt;&lt;span class="caps"&gt;JADE&lt;/span&gt;&lt;/a&gt;.  However, most people I know don’t really like working with &lt;span class="caps"&gt;JADE&lt;/span&gt; (yes,
that’s an&amp;nbsp;euphemism).&lt;/p&gt;
&lt;p&gt;I also didn’t find a modern, well documented and easy-to-use Python library for
the job.  So I asked my self: “How hard would it be to create one on my&amp;nbsp;own?”&lt;/p&gt;
&lt;p&gt;Being one of the maintainers of the discrete-event simulation &lt;em&gt;(&lt;span class="caps"&gt;DES&lt;/span&gt;)&lt;/em&gt; library
&lt;a href="https://pypi.python.org/pypi/SimPy"&gt;SimPy&lt;/a&gt;, I already have some experience with event-based and asynchronous
programming.  Multi-agent systems are relatively similar to &lt;span class="caps"&gt;DES&lt;/span&gt;, so I thought
I should give it a&amp;nbsp;try.&lt;/p&gt;
&lt;p&gt;I also wanted to get to know &lt;em&gt;asyncio&lt;/em&gt;.  I’ve never worked with it before but
its concepts looked very similar to the ones we’ve been using in SimPy for
years, so building a library for multi-agent systems on top of asyncio was
a perfect project to learn something&amp;nbsp;new.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-do-i-use-it"&gt;
&lt;h2&gt;How do I use&amp;nbsp;it?&lt;/h2&gt;
&lt;p&gt;The basic idea behind &lt;em&gt;aiomas&lt;/em&gt; is that agents should be plain Python classes.
Methods that perform asynchronous operations (like sending messages to other
agents) should be coroutines.  Agents should be able to call methods that other
agents expose.  It should also be easy to distribute agents over multiple &lt;span class="caps"&gt;CPU&lt;/span&gt;
cores or machines.  But you, as the user, shouldn’t bothered with setting up
sockets or implementing transports, protocols and other low-level&amp;nbsp;stuff.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;aiomas&lt;/em&gt; is still in a relatively early stage of development.  What’s already
there seems to work quite nicely,&amp;nbsp;though.&lt;/p&gt;
&lt;p&gt;Here is a simple example how it currently looks&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;aiomas&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;container&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="gp"&gt;... &lt;/span&gt;        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&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="gp"&gt;... &lt;/span&gt;        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ohai, I am &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="nd"&gt;@asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coroutine&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&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;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;The agent&amp;#39;s &amp;quot;main&amp;quot; function.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="n"&gt;remote_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield from&lt;/span&gt; &lt;span class="n"&gt;remote_agent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; from &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="nd"&gt;@aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expose&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;service&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;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Exposed function that can be called by remote agents.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# A Container is the home for a number of agents.&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiomas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5555&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestAgent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="go"&gt;Ohai, I am agent://localhost:5555/0&lt;/span&gt;
&lt;span class="go"&gt;Ohai, I am agent://localhost:5555/1&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# Run agent 0 and let it call a method from agent 1&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;agent://localhost:5555/1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;agent://localhost:5555/0 got 42 from agent://localhost:5555/1&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="whats-the-list-of-features"&gt;
&lt;h2&gt;What’s the list of&amp;nbsp;features?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;aiomas&lt;/em&gt; just puts three layers of abstraction around raw &lt;span class="caps"&gt;TCP&lt;/span&gt; / unix domain
sockets provided by &lt;em&gt;asyncio&lt;/em&gt;:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;Request-reply&amp;nbsp;channel:&lt;/dt&gt;
&lt;dd&gt;The &lt;em&gt;channel&lt;/em&gt; layer is the basis for the &lt;em&gt;rpc&lt;/em&gt; layer. It sends &lt;a href="http://www.json.org/"&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt;&lt;/a&gt; or
&lt;a href="http://msgpack.org/"&gt;MsgPack&lt;/a&gt; encoded byte strings over &lt;span class="caps"&gt;TCP&lt;/span&gt; or unix domain sockets. It also maps
replies (of success or failure) to their corresponding&amp;nbsp;request.&lt;/dd&gt;
&lt;dt&gt;&lt;span class="caps"&gt;RPC&lt;/span&gt;:&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;The &lt;em&gt;rpc&lt;/em&gt; layer implements remote procedure calls which let you call methods
on remote objects nearly as if they were normal&amp;nbsp;objects:&lt;/p&gt;
&lt;p&gt;Instead of &lt;code&gt;ret = obj.meth(arg)&lt;/code&gt; you write &lt;code&gt;ret = yield from
obj.meth(arg)&lt;/code&gt;.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Agents and agent&amp;nbsp;containers:&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;The top-layer provides a simple base class for your own agents. All agents
live in a&amp;nbsp;container.&lt;/p&gt;
&lt;p&gt;Containers take care of creating agents and performing the communication
between&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;The container provides a &lt;em&gt;clock&lt;/em&gt; for the agents. This clock can either be
synchronized with the real (wall-clock) time or be set by an external process
(e.g., other&amp;nbsp;simulators).&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Although you usually want to use the &lt;em&gt;agent&lt;/em&gt; layer, it is perfectly okay to
only use the &lt;em&gt;rpc&lt;/em&gt; or &lt;em&gt;channel&lt;/em&gt;&amp;nbsp;layer.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="whats-comming-next"&gt;
&lt;h2&gt;What’s comming&amp;nbsp;next?&lt;/h2&gt;
&lt;p&gt;Some ideas for future&amp;nbsp;releases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="caps"&gt;SSL&lt;/span&gt;/&lt;span class="caps"&gt;TLS&lt;/span&gt; support for &lt;span class="caps"&gt;TCP&lt;/span&gt;&amp;nbsp;sockets&lt;/li&gt;
&lt;li&gt;Optional automatic re-connect after connection&amp;nbsp;loss&lt;/li&gt;
&lt;li&gt;Helper for binding a socket to a random free&amp;nbsp;port&lt;/li&gt;
&lt;li&gt;Implement a queue based transport that agents within a single container can
use instead of a &lt;span class="caps"&gt;TCP&lt;/span&gt;&amp;nbsp;connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="installation"&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;aiomas&lt;/em&gt; requires Python &amp;gt;= 3.4, &lt;a href="https://pypi.python.org/pypi/msgpack-python"&gt;msgpack-python&lt;/a&gt;  and &lt;a href="https://pypi.python.org/pypi/arrow"&gt;arrow&lt;/a&gt; (it may also run
on Python 3.3 with the &lt;a href="https://pypi.python.org/pypi/asyncio"&gt;asyncio&lt;/a&gt; package, but this is&amp;nbsp;untested).&lt;/p&gt;
&lt;p&gt;Install &lt;em&gt;aiomas&lt;/em&gt; via &lt;a href="https://pip.pypa.io/"&gt;pip&lt;/a&gt; by&amp;nbsp;running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;aiomas
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The development happens on &lt;a href="https://bitbucket.org/sscherfke/aiomas"&gt;bitbucket&lt;/a&gt;.  There’s no mailing list or &lt;span class="caps"&gt;IRC&lt;/span&gt; channel
yet, so just create an issue for your feedback, send me an email, or leave me
a &lt;a href="https://twitter.com/sscherfke"&gt;tweet&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2015"></category><category term="python"></category><category term="scientific"></category><category term="aiomas"></category><category term="mas"></category></entry><entry><title>Master(ing) Passwords</title><link href="https://stefan.sofa-rockers.org/2015/02/01/master-passwords/" rel="alternate"></link><published>2015-02-01T13:37:00+01:00</published><updated>2015-02-01T13:37:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2015-02-01:/2015/02/01/master-passwords/</id><summary type="html">&lt;p&gt;Using good passwords is hard. You can’t remember complicated ones
but you also don’t want store them in the cloud. Biometry is not
a solution either. The &lt;em&gt;master password&lt;/em&gt; algorithm comes to help.
There are various implementations of it and I did one in&amp;nbsp;Python.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Good passwords should be only known to you, they should be hard to guess, you
should have different ones for each service and you &lt;a href="http://en.wikipedia.org/wiki/2012_LinkedIn_hack"&gt;often&lt;/a&gt; &lt;a href="http://www.reuters.com/article/2014/05/21/us-ebay-password-idUSBREA4K0B420140521"&gt;need&lt;/a&gt; &lt;a href="http://www.nbcnews.com/tech/security/billion-passwords-stolen-change-all-yours-now-n174321"&gt;to
change&lt;/a&gt; &lt;a href="http://lifehacker.com/5-million-gmail-passwords-leaked-check-yours-now-1632983265"&gt;them&lt;/a&gt;.  Meeting all these requirements is hard and people often
&lt;a href="http://cir.ca/news/most-commonly-used-passwords"&gt;fail&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some people claim that biometrics (like finger prints, facial recognition or
iris scans) can help you out of this misery, but they are wrong.  You can’t
keep your face (and even your finger prints) private.  Worse, you can’t change
them if someone starts to misuse them.  So they can, at best, be used as a kind
of user&amp;nbsp;name.&lt;/p&gt;
&lt;p&gt;You can use password safes to store complicate passwords for all the web
service you use.  But your app may use a proprietary container format.  What do
you do if the app becomes obsolete and all of your passwords are stored in
a format no other software can read?  And can you really be sure that no one
can read your passwords that you uploaded into the&amp;nbsp;cloud?&lt;/p&gt;
&lt;p&gt;A relatively new approach to solve this problem is the &lt;a href="http://masterpasswordapp.com/algorithm.html"&gt;Master Password&lt;/a&gt;
algorithm by &lt;em&gt;Maarten Billemont&lt;/em&gt;.  Its basic idea is strikingly simple:  You
have one (complicated) password that you have to remember.  Based on that
password, the algorithm generates site or service specific passwords of any
complexity – ranging from four digit PINs to 20 characters long all-random
passwords.  Given the same input, the algorithm always generates the same
passwords.  Since there’s only one secret involved in the whole process (your
master password that you keep in your head), no passwords need to be stored
anywhere and yet you can use Master Password from all your devices if you have
an appropriate app&amp;nbsp;available.&lt;/p&gt;
&lt;p&gt;I read about the Master Password algorithm in August 2014 in the German &lt;span class="caps"&gt;IT&lt;/span&gt;
magazine &lt;a href="http://www.heise.de/ct/ausgabe/2014-18-Ein-neues-Konzept-fuer-den-Umgang-mit-Passwoertern-2284364.html"&gt;c’t&lt;/a&gt; and got curious how hard it would be to implement the
algorithm in Python (3).  It turned to be quite straightforward.  After toying
around with it for a while, I have now a relatively stable and useful master
password library and, on top of it, a command line&amp;nbsp;interface.&lt;/p&gt;
&lt;section id="available-implementations"&gt;
&lt;h2&gt;Available&amp;nbsp;implementations&lt;/h2&gt;
&lt;p&gt;Before I go into detail about it, I want to list all available implementations
I’m aware of (please contact me if I forgot&amp;nbsp;one):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Official implementations (the source code for all of them can be found on
&lt;a href="https://github.com/Lyndir/MasterPassword"&gt;github&lt;/a&gt;):&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;span class="caps"&gt;OS&lt;/span&gt; X &lt;span class="caps"&gt;GUI&lt;/span&gt;:&lt;/strong&gt; It is available via the &lt;a href="https://itunes.apple.com/app/id662763204"&gt;Mac App Store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;iOS App:&lt;/strong&gt; It is available via the &lt;a href="https://itunes.apple.com/app/id510296984"&gt;iTunes Store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java &lt;span class="caps"&gt;GUI&lt;/span&gt;:&lt;/strong&gt; Supports Mac, Linux, Unix, and Windows and can be downloaded
&lt;a href="https://ssl.masterpasswordapp.com/masterpassword-gui.jar"&gt;from the official website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native C &lt;span class="caps"&gt;CLI&lt;/span&gt;:&lt;/strong&gt; Supports Mac, Linux, and Posix and can be downloaded
&lt;a href="https://ssl.masterpasswordapp.com/masterpassword-cli.tar.gz"&gt;from the official website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android App (beta):&lt;/strong&gt; Available via the &lt;a href="https://ssl.masterpasswordapp.com/masterpassword-android.apk"&gt;official website&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JavaScript web app (beta):&lt;/strong&gt; Available via the &lt;a href="https://js.masterpasswordapp.com/"&gt;official website&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Alternative implementations:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Master Password for Android:&lt;/strong&gt; An alternative Android client with a nicer
&lt;span class="caps"&gt;GUI&lt;/span&gt; (in Lollipop’s Material Design).  Available as &lt;a href="https://play.google.com/store/apps/details?id=de.devland.masterpassword"&gt;free&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=de.devland.masterpassword.pro"&gt;pro&lt;/a&gt; version
in Google’s Play&amp;nbsp;Store.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;qMasterPassword:&lt;/strong&gt; A &lt;span class="caps"&gt;QT&lt;/span&gt; based &lt;span class="caps"&gt;GUI&lt;/span&gt; written in C++. It should be platform
independent but unfortunately doesn’t provide any binaries. You can grab
the source from &lt;a href="https://github.com/bkueng/qMasterPassword"&gt;github&lt;/a&gt; and compile it on your&amp;nbsp;own.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chrome Extension:&lt;/strong&gt; A Chrome extension available in the &lt;a href="https://chrome.google.com/webstore/detail/chrome-passwords/injddhenpdfbccendcpjghdclnadepnp"&gt;Chrome Web
Store&lt;/a&gt;.  The source can be found on &lt;a href="https://github.com/brightdroid/chrome-passwords"&gt;github&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Python &lt;span class="caps"&gt;CLI&lt;/span&gt; and library:&lt;/strong&gt; My own implementation in Python.  It runs on &lt;span class="caps"&gt;OS&lt;/span&gt;
X, Linux and Windows.  You can get it via the &lt;a href="https://pypi.python.org/pypi/mpw/"&gt;PyPI&lt;/a&gt;.  The sources are on
&lt;a href="https://bitbucket.org/sscherfke/mpw/"&gt;bitbucket&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, you can use Master Password on most of the common platforms and&amp;nbsp;devices.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="mpw-0-3"&gt;
&lt;h2&gt;mpw&amp;nbsp;0.3&lt;/h2&gt;
&lt;p&gt;After two prototypes, my &lt;em&gt;mpw&lt;/em&gt; package has now reached a certain level of
maturity and provides some comfort.  It’s still labeled as &lt;em&gt;alpha&lt;/em&gt;, though,
because some features I’d like to implement are still&amp;nbsp;missing.&lt;/p&gt;
&lt;p&gt;For your convenience, &lt;em&gt;mpw&lt;/em&gt; remembers the sites and users that you created,
so the first thing you have to do is creating a user (you can have multiple)
and some sites for&amp;nbsp;them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mpw&lt;span class="w"&gt; &lt;/span&gt;adduser&lt;span class="w"&gt; &lt;/span&gt;Alice
Enter&lt;span class="w"&gt; &lt;/span&gt;master&lt;span class="w"&gt; &lt;/span&gt;password:
Confirm&lt;span class="w"&gt; &lt;/span&gt;master&lt;span class="w"&gt; &lt;/span&gt;password:
Added&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;.
$&lt;span class="w"&gt; &lt;/span&gt;mpw&lt;span class="w"&gt; &lt;/span&gt;addsite&lt;span class="w"&gt; &lt;/span&gt;test-site
Enter&lt;span class="w"&gt; &lt;/span&gt;master&lt;span class="w"&gt; &lt;/span&gt;password&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;:
Added&lt;span class="w"&gt; &lt;/span&gt;site&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test-site&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;mpw&lt;/em&gt; stores an scrypt hash of your master key.  It will then tell you when you
make typos while entering your master password.  By default, newly created
sites will use the &lt;em&gt;long&lt;/em&gt; password type.  Run &lt;code&gt;mpw addsite --help&lt;/code&gt; to get
more information on that.  The sites and their settings are stored encrypted on
your disk (using &lt;a href="https://cryptography.io/en/latest/fernet/"&gt;cryptography Fernet&lt;/a&gt;).  Note, that these steps only
simplify the later usage and are not required by the&amp;nbsp;algorithm.&lt;/p&gt;
&lt;p&gt;To actually generate a password for Alice’ test-site account,&amp;nbsp;run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mpw&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;test-site
Enter&lt;span class="w"&gt; &lt;/span&gt;master&lt;span class="w"&gt; &lt;/span&gt;password&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;:
Password&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;test-site&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;copied&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;clipboard.
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As the output suggest, the command copies the password into your clipboard.
You can then simply paste it into a login&amp;nbsp;form.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;mpw&lt;/em&gt; can also print the generated password to &lt;code&gt;stdout&lt;/code&gt;.  This way you can
pipe it directly to other commands like &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mpw&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;server-root&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;/etc/crontab
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The command &lt;code&gt;mpw --help&lt;/code&gt; prints a list of all commands that are currently&amp;nbsp;supported.&lt;/p&gt;
&lt;p&gt;Here is a list of all available&amp;nbsp;features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports multiple versions of the algorithm.  If the algorithm evolves, you
can still generated password for older sites using an older version of the
algorithm.  Currently, version 1 and 2 are&amp;nbsp;implemented.&lt;/li&gt;
&lt;li&gt;Generated passwords are copied into your clipboard by default.  You can also
print them to &lt;code&gt;stdout&lt;/code&gt; and pipe them into other&amp;nbsp;commands.&lt;/li&gt;
&lt;li&gt;Lets you add, delete and list users.  You can set a user as default user and
you can change their master password (which will invalidate, by the very
nature of the algorithm, all previously generated passwords.  Sometimes this
is&amp;nbsp;necessary).&lt;/li&gt;
&lt;li&gt;Lets you add, delete and list sites for a specific user.  Settings for a site
can be&amp;nbsp;updated.&lt;/li&gt;
&lt;li&gt;Stores a hashed version of a user’s master key on disk in order to tell you
when you made a typo in your master password (which would generate the wrong
site specific password).  &lt;a href="https://pypi.python.org/pypi/scrypt/"&gt;scrypt&lt;/a&gt; is used for&amp;nbsp;that.&lt;/li&gt;
&lt;li&gt;Stores the sites and site settings of a user encrypted.  &lt;a href="https://cryptography.io/en/latest/fernet/"&gt;cryptography
Fernet&lt;/a&gt; is used for&amp;nbsp;that.&lt;/li&gt;
&lt;li&gt;Can also be used as a library by other&amp;nbsp;programs.&lt;/li&gt;
&lt;li&gt;100% line and branch coverage.  Multiple tests to make sure it generates the
same passwords as the reference&amp;nbsp;implementation.&lt;/li&gt;
&lt;li&gt;Written for Python 3.4 and&amp;nbsp;above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And these things are planned for the&amp;nbsp;future:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add import and export functionality ((un)encrypted &lt;span class="caps"&gt;JSON&lt;/span&gt; and the format
that the &lt;span class="caps"&gt;OS&lt;/span&gt; X app is currently&amp;nbsp;using).&lt;/li&gt;
&lt;li&gt;Allow the generation of login&amp;nbsp;names.&lt;/li&gt;
&lt;li&gt;Allow the generation of answers for security&amp;nbsp;questions.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Maybe&lt;/em&gt; a &lt;span class="caps"&gt;GUI&lt;/span&gt; using PyQT that runs under &lt;span class="caps"&gt;OS&lt;/span&gt; X, Linux and Windows.  But since
there are already plenty of GUIs and I don’t have that much time, I may not
do it.  Please contact me if you are interested in helping me out&amp;nbsp;here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can grab your copy of &lt;em&gt;mpw&lt;/em&gt; from the &lt;a href="https://pypi.python.org/pypi/mpw/"&gt;Python Package Index&lt;/a&gt;. I recommend
installing it via &lt;a href="https://github.com/mitsuhiko/pipsi"&gt;pipsi&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pipsi&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--python&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mpw
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But you can also use &lt;code&gt;pip&lt;/code&gt; to install it globally or into a&amp;nbsp;virtualenv:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;mpw
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Warning:&lt;/em&gt; If you already used &lt;em&gt;mpw 0.2&lt;/em&gt; and have some sites configured,
run &lt;a href="http://pastebin.com/pBBaGU6E"&gt;this script&lt;/a&gt; to upgrade you config file.  Run it &lt;em&gt;after&lt;/em&gt; you installed
&lt;em&gt;mpw 0.3&lt;/em&gt; but before actually using&amp;nbsp;it.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2015"></category><category term="python"></category><category term="masterpassword"></category><category term="mpw"></category></entry><entry><title>SimPy: Real-time simulations</title><link href="https://stefan.sofa-rockers.org/2014/06/25/simpy-real-time-simulations/" rel="alternate"></link><published>2014-06-25T10:15:00+02:00</published><updated>2014-06-25T10:15:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-06-25:/2014/06/25/simpy-real-time-simulations/</id><summary type="html">&lt;p&gt;&lt;a href="https://simpy.readthedocs.org"&gt;SimPy&lt;/a&gt; is a discrete-event
simulation library for Python. It can perform simulations &lt;em&gt;as fast
as possible&lt;/em&gt; as well as in &lt;em&gt;real time&lt;/em&gt; (or &lt;em&gt;wall-clock time&lt;/em&gt;). This
guide describes &lt;em&gt;real-time simulations&lt;/em&gt; can be achieved in&amp;nbsp;SimPy.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is another guide to SimPy simulations. SimPy ususally runs simulations as
fast as possible, but sometimes, you might want your simulation to run
synchronized with the wall-clock time. This kind of simulation is also called
&lt;em&gt;real-time simulation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Real-time simulations may be&amp;nbsp;necessary&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;if you have&amp;nbsp;hardware-in-the-loop,&lt;/li&gt;
&lt;li&gt;if there is human interaction with your simulation,&amp;nbsp;or&lt;/li&gt;
&lt;li&gt;if you want to analyze the real-time behavior of an&amp;nbsp;algorithm.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To convert a simulation into a real-time simulation, you only need to replace
SimPy’s default &lt;code&gt;Environment&lt;/code&gt; with a &lt;code&gt;simpy.rt.RealtimeEnvironment&lt;/code&gt;.  Apart
from the &lt;em&gt;initial_time&lt;/em&gt; argument, there are two additional parameters: &lt;em&gt;factor&lt;/em&gt;
and &lt;em&gt;strict&lt;/em&gt;: &lt;code&gt;RealtimeEnvironment(initial_time=0, factor=1.0, strict=True)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;factor&lt;/em&gt; defines how much &lt;em&gt;real time&lt;/em&gt; passes with each step of simulation
time. By default, this is one second. If you set &lt;code&gt;factor=0.1&lt;/code&gt;, a unit of
simulation time will only take a tenth of a second; if you set &lt;code&gt;factor=60&lt;/code&gt;,
it will take a&amp;nbsp;minute.&lt;/p&gt;
&lt;p&gt;Here is a simple example for converting a normal simulation to a real-time
simulation with a duration of one tenth of a second per simulation time&amp;nbsp;unit:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Duration of one simulation time unit: &lt;/span&gt;&lt;span class="si"&gt;%.2f&lt;/span&gt;&lt;span class="s1"&gt;s&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;simulation&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy.rt&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RealtimeEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="n"&gt;simulation&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the &lt;em&gt;strict&lt;/em&gt; parameter is set to &lt;code&gt;True&lt;/code&gt; (the default), the &lt;code&gt;step()&lt;/code&gt; and
&lt;code&gt;run()&lt;/code&gt; methods will raise a &lt;code&gt;RuntimeError&lt;/code&gt; if the computation within
a simulation time step take more time than the real-time factor allows. In the
following example, a process will perform a task that takes 0.02 seconds within
a real-time environment with a time factor of 0.01&amp;nbsp;seconds:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy.rt&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slow_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Heavy computation :-)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RealtimeEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slow_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Everything alright&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Simulation is too slow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Simulation&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;too&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To suppress the error, simply set &lt;code&gt;strict=False&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RealtimeEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slow_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Everything alright&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Simulation is too slow&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Everything&lt;/span&gt; &lt;span class="n"&gt;alright&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s it. Real-time simulations are that simple with SimPy! This guide is now
also part of the &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/real-time-simulations.html"&gt;official SimPy documentation&lt;/a&gt;.&lt;/p&gt;
</content><category term="2014"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>SimPy: Shared Resources</title><link href="https://stefan.sofa-rockers.org/2014/06/13/simpy-shared-resources/" rel="alternate"></link><published>2014-06-13T21:18:00+02:00</published><updated>2014-06-13T21:18:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-06-13:/2014/06/13/simpy-shared-resources/</id><summary type="html">&lt;p&gt;&lt;a href="https://simpy.readthedocs.org"&gt;SimPy&lt;/a&gt; is a discrete-event
simulation library for Python. This guide describes its shared
resources and shows how you can use them to model things like
producer/consumer&amp;nbsp;problems.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Shared resources are another way to model &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/process_interaction.html"&gt;process interaction&lt;/a&gt;.
They form a congestion point where processes queue up in order to use&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;SimPy defines three categories of&amp;nbsp;resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#resources"&gt;Resources&lt;/a&gt; – Resources that can be used by a limited
number of processes at a time (e.g., a gas station with a limited number of
fuel&amp;nbsp;pumps).&lt;/li&gt;
&lt;li&gt;&lt;a href="#containers"&gt;Containers&lt;/a&gt; – Resources that model the production and
consumption of a homogeneous, undifferentiated bulk. It may either be
continuous (like water) or discrete (like&amp;nbsp;apples).&lt;/li&gt;
&lt;li&gt;&lt;a href="#stores"&gt;Stores&lt;/a&gt; – Resources that allow the production and
consumption of Python&amp;nbsp;objects.&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="the-basic-concept-of-resources"&gt;
&lt;h2&gt;The basic concept of&amp;nbsp;resources&lt;/h2&gt;
&lt;p&gt;All resources share the same basic concept: The resource itself is some kind of
a container with a, usually limited, &lt;em&gt;capacity&lt;/em&gt;. Processes can either try to
&lt;em&gt;put&lt;/em&gt; something into the resource or try to &lt;em&gt;get&lt;/em&gt; something out. If the
resource is full or empty, they have to &lt;em&gt;queue&lt;/em&gt; up and&amp;nbsp;wait.&lt;/p&gt;
&lt;p&gt;This is roughly, how every resource looks&amp;nbsp;like:&lt;/p&gt;
&lt;pre&gt;BaseResource(capacity):
   put_queue
   get_queue

   put(): event
   get(): event&lt;/pre&gt;
&lt;p&gt;Every resources a maximum capacity and two queues, one for processes that want
to put something into it and one for processes that want to get something out.
The &lt;code&gt;put()&lt;/code&gt; and &lt;code&gt;get()&lt;/code&gt; methods both return an event that is triggered when
the corresponding action was&amp;nbsp;successful.&lt;/p&gt;
&lt;section id="resources-and-interrupts"&gt;
&lt;h3&gt;Resources and&amp;nbsp;interrupts&lt;/h3&gt;
&lt;p&gt;While a process is waiting for a put or get event to succeed, it may be
&lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/process_interaction.html#interrupting-another-process"&gt;interrupted&lt;/a&gt;  by another process. After catching the interrupt, the process
has two&amp;nbsp;possibilities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It may continue to wait for the request (by yielding the event&amp;nbsp;again).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It may stop waiting for the request. In this case, it has to call the
event’s &lt;code&gt;cancel()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Since you can easily forget this, all resources events are &lt;em&gt;context
managers&lt;/em&gt; (see the &lt;a href="https://docs.python.org/3/reference/compound_stmts.html#with"&gt;Python docs&lt;/a&gt; for&amp;nbsp;details).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;The resource system is modular and extensible. Resources can, for example, use
specialized queues and event types. This allows them to use sorted queues, to
add priorities to events, or to offer&amp;nbsp;preemption.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="resources"&gt;
&lt;h2&gt;Resources&lt;/h2&gt;
&lt;p&gt;Resources can be used by a limited number of processes at a time (e.g.,
a gas station with a limited number of fuel pumps). Processes &lt;em&gt;request&lt;/em&gt; these
resources to become a user (or to “own” them) and have to &lt;em&gt;release&lt;/em&gt; them once
they are done (e.g., vehicles arrive at the gas station, use a fuel-pump, if
one is available, and leave when they are&amp;nbsp;done).&lt;/p&gt;
&lt;p&gt;Requesting a resources is modeled as “putting a process’ token into the
resources” and releasing a resources correspondingly as “getting a process’
token out of the resource”. Thus, calling &lt;code&gt;request()&lt;/code&gt;/&lt;code&gt;release()&lt;/code&gt; is
equivalent to calling &lt;code&gt;put()&lt;/code&gt;/&lt;code&gt;get()&lt;/code&gt;. Releasing a resource will always
succeed&amp;nbsp;immediately.&lt;/p&gt;
&lt;p&gt;SimPy implements three &lt;em&gt;resource&lt;/em&gt;&amp;nbsp;types:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.html#simpy.resources.resource.Resource"&gt;Resource&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.html#simpy.resources.resource.PriorityResource"&gt;PriorityResource&lt;/a&gt;, where queueing processes are sorted by&amp;nbsp;priority&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.html#simpy.resources.resource.PreemptiveResource"&gt;PreemptiveResource&lt;/a&gt;, where processes additionally may preempt other
processes with a lower&amp;nbsp;priority&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="resource"&gt;
&lt;h3&gt;Resource&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;Resource&lt;/code&gt; is conceptually a &lt;em&gt;semaphore&lt;/em&gt;. Its only parameter – apart from
the obligatory reference to an &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment"&gt;Environment&lt;/a&gt; – is its &lt;em&gt;capacity&lt;/em&gt;. It must be
a positive number and defaults to 1: &lt;code&gt;Resource(env, capacity=1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of just counting its current users, it stores the request event as an
“access token” for each user. This is, for example, useful for adding
preemption (see&amp;nbsp;below).&lt;/p&gt;
&lt;p&gt;Here is as basic example for using a&amp;nbsp;resource:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Generate a request event&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;                 &lt;span class="c1"&gt;# Wait for access&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&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;# Do something&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# Release the resource&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note, that you have to release the resource under all conditions; for example,
if you got interrupted while waiting for or using the resource. In order to
help you with that and to avoid too many &lt;code&gt;try: ... finally: ...&lt;/code&gt; constructs,
request events can be used as context&amp;nbsp;manager:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Generate a request event&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;                    &lt;span class="c1"&gt;# Wait for access&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&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;# Do something&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;                                      &lt;span class="c1"&gt;# Resource released automatically&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Resources allow you retrieve the list of users and queued as well as the
number of users and resource’s&amp;nbsp;capacity:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt; of &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt; slots are allocated.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;  Users:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;  Queued events:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;procs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;allocated&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
  &lt;span class="n"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="n"&gt;Queued&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="priorityresource"&gt;
&lt;h3&gt;PriorityResource&lt;/h3&gt;
&lt;p&gt;As you may know from the real world, not every one is equally important. To map
that to SimPy, there’s the &lt;em&gt;PriorityResource&lt;/em&gt;. This subclass of &lt;em&gt;Resource&lt;/em&gt; lets
requesting processes provide a priority for each request. More important
requests will gain access to the resource earlier than less important ones.
Priority is expressed by integer numbers; smaller numbers mean a higher&amp;nbsp;priority.&lt;/p&gt;
&lt;p&gt;Apart form that, it works like a normal &lt;em&gt;Resource&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource_user&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; requesting at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; with priority=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got resource at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PriorityResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&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="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Although &lt;em&gt;p3&lt;/em&gt; requested the resource later than &lt;em&gt;p2&lt;/em&gt;, it could use it earlier
because its priority was&amp;nbsp;higher.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="preemptiveresource"&gt;
&lt;h3&gt;PreemptiveResource&lt;/h3&gt;
&lt;p&gt;Sometimes, new requests are so important that queue-jumping is not enough and
they need to kick existing users out of the resource (this is called
&lt;em&gt;preemption&lt;/em&gt;). The &lt;em&gt;PreemptiveResource&lt;/em&gt; allows you to do exactly&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resource_user&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; requesting at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; with priority=&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got resource at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage_since&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got preempted by &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; after &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;
&lt;span class="o"&gt;...&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="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreemptiveResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&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="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;preempted&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;PreemptiveResource&lt;/em&gt; inherits from &lt;em&gt;PriorityResource&lt;/em&gt; and adds a &lt;em&gt;preempt&lt;/em&gt;
flag (that defaults to &lt;code&gt;True&lt;/code&gt;) to &lt;code&gt;request()&lt;/code&gt;. By setting this to &lt;code&gt;False&lt;/code&gt;
(&lt;code&gt;resource.request(priority=x, preempt=False)&lt;/code&gt;), a process can decide to not
preempt another resource user. It will still be put in the queue according to
its priority,&amp;nbsp;though.&lt;/p&gt;
&lt;p&gt;The implementation of &lt;em&gt;PreemptiveResource&lt;/em&gt; values priorities higher than
preemption. That means preempt request are not allowed to cheat and jump over
a higher prioritized request. The following example shows that preemptive low
priority requests cannot queue-jump over high priority&amp;nbsp;requests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preempt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preempt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;preempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; requesting at &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got resource at &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got preempted at &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreemptiveResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preempt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&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;# Give A a head start&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preempt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prio&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="n"&gt;preempt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;C&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Process &lt;em&gt;A&lt;/em&gt; requests the resource with priority 0. It immediately becomes
a&amp;nbsp;user.&lt;/li&gt;
&lt;li&gt;Process &lt;em&gt;B&lt;/em&gt; requests the resource with priority -2 but sets &lt;em&gt;preempt&lt;/em&gt; to
&lt;code&gt;False&lt;/code&gt;. It will queue up and&amp;nbsp;wait.&lt;/li&gt;
&lt;li&gt;Process &lt;em&gt;C&lt;/em&gt; requests the resource with priority -1 but leaves &lt;em&gt;preempt&lt;/em&gt;
&lt;code&gt;True&lt;/code&gt;. Normally, it would preempt &lt;em&gt;A&lt;/em&gt; but in this case, &lt;em&gt;B&lt;/em&gt; is queued up
before &lt;em&gt;C&lt;/em&gt; and prevents &lt;em&gt;C&lt;/em&gt; from preempting &lt;em&gt;A&lt;/em&gt;. &lt;em&gt;C&lt;/em&gt; can also not preempt
&lt;em&gt;B&lt;/em&gt; since its priority is not high&amp;nbsp;enough.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thus, the behavior in the example is the same as if no preemption was used at
all. Be careful when using mixed&amp;nbsp;preemption!&lt;/p&gt;
&lt;p&gt;Due to the higher priority of process &lt;em&gt;B&lt;/em&gt;, no preemption occurs in this
example. Note that an additional request with a priority of -3 would be able
to preempt &lt;em&gt;A&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If your use-case requires a different behaviour, for example queue-jumping or
valuing preemption over priorities, you can subclass &lt;em&gt;PreemptiveResource&lt;/em&gt; and
override the default&amp;nbsp;behaviour.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="containers"&gt;
&lt;h2&gt;Containers&lt;/h2&gt;
&lt;p&gt;Containers help you modelling the production and consumption of a homogeneous,
undifferentiated bulk. It may either be continuous (like water) or discrete
(like&amp;nbsp;apples).&lt;/p&gt;
&lt;p&gt;You can use this, for example, to model the gas / petrol tank of a gas station.
Tankers increase the amount of gasoline in the tank while cars decrease&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;The following example is a very simple model of a gas station with a limited
number of fuel dispensers (modeled as &lt;code&gt;Resource&lt;/code&gt;) and a tank modeled as
&lt;code&gt;Container&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GasStation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fuel_dispensers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mon_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor_tank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monitor_tank&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;                 &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Calling tanker at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;                 &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tanker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&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="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tanker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Need 10 Minutes to arrive&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Tanker arriving at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;car&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Car &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; arriving at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fuel_dispensers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Car &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; starts refueling at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gas_tank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Car &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; done refueling at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;car_generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GasStation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;car_gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;car_generator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gas_station&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;arriving&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;arriving&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;arriving&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;Calling&lt;/span&gt; &lt;span class="n"&gt;tanker&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;arriving&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;Tanker&lt;/span&gt; &lt;span class="n"&gt;arriving&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;refueling&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Containers allow you to retrieve their current &lt;code&gt;level&lt;/code&gt; as well as their
&lt;code&gt;capacity&lt;/code&gt; (see &lt;code&gt;GasStation.monitor_tank()&lt;/code&gt; and &lt;code&gt;tanker()&lt;/code&gt;). You can also
access the list of waiting events via the &lt;code&gt;put_queue&lt;/code&gt; and &lt;code&gt;get_queue&lt;/code&gt;
attributes (similar to &lt;code&gt;Resource.queue&lt;/code&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="stores"&gt;
&lt;h2&gt;Stores&lt;/h2&gt;
&lt;p&gt;Using Stores you can model the production and consumption of concrete objects
(in contrast to the rather abstract “amount” stored in containers). A single
Store can even contain multiple types of&amp;nbsp;objects.&lt;/p&gt;
&lt;p&gt;Beside &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.html#simpy.resources.store.Store"&gt;Store&lt;/a&gt;, there is a &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.html#simpy.resources.store.FilterStore"&gt;FilterStore&lt;/a&gt; that lets you use a custom
function to filter the objects you get out of the&amp;nbsp;store.&lt;/p&gt;
&lt;p&gt;Here is a simple example modelling a generic producer/consumer&amp;nbsp;scenario:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;spam &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Produced spam at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;consumer&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&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="s1"&gt;&amp;#39;requesting spam at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&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="s1"&gt;&amp;#39;got&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;Produced&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;requesting&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;Produced&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As with the other resource types, you can get a store’s capacity via the
&lt;code&gt;capacity&lt;/code&gt; attribute. The attribute &lt;code&gt;items&lt;/code&gt; points to the list of items
currently available in the store. The put and get queues can be accessed via
the &lt;code&gt;put_queue&lt;/code&gt; and &lt;code&gt;get_queue&lt;/code&gt; attributes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;FilterStore&lt;/em&gt; can, for example, be used to model machine shops where machines
have varying attributes. This can be useful if the homogeneous slots of
a &lt;em&gt;Resource&lt;/em&gt; are not what you&amp;nbsp;need:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Machine&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;size, duration&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Small and slow&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&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;# Big and fast&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;machine_shop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FilterStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;machine_shop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Pre-populate the machine shop&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;user&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="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&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="s1"&gt;&amp;#39;got&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&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="s1"&gt;&amp;#39;released&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine_shop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&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="o"&gt;...&lt;/span&gt;          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&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="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;released&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&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="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;released&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;released&lt;/span&gt; &lt;span class="n"&gt;Machine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This guide is now also &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/resources.html"&gt;part&lt;/a&gt; of SimPy’s &lt;a href="http://simpy.readthedocs.org/en/latest/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2014"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>SimPy: Process Interaction</title><link href="https://stefan.sofa-rockers.org/2014/04/11/simpy-process-interaction/" rel="alternate"></link><published>2014-04-11T21:08:00+02:00</published><updated>2014-04-11T21:08:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-04-11:/2014/04/11/simpy-process-interaction/</id><summary type="html">&lt;p&gt;&lt;a href="https://simpy.readthedocs.org"&gt;SimPy&lt;/a&gt; is a discrete-event
simulation library for Python. This guide describes how you can let
processes interact with each other – because this is what makes
simulation&amp;nbsp;fun!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;As promised, the delay between this and the &lt;a href="/2014/04/05/simpy-events/"&gt;last topical guide&lt;/a&gt; was rather&amp;nbsp;short.&lt;/p&gt;
&lt;p&gt;This one is about process interaction. This is what makes event discrete
simulation interesting. Without it, you actually wouldn’t even need &lt;a href="https://simpy.readthedocs.org"&gt;SimPy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So this guide is&amp;nbsp;about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#sleep-until-woken-up"&gt;sleep-until-woken-up&lt;/a&gt;&amp;nbsp;(passivate/reactivate)&lt;/li&gt;
&lt;li&gt;&lt;a href="#waiting-for-another-process-to-terminate"&gt;waiting-for-another-process-to-terminate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#interrupting-another-process"&gt;interrupting-another-process&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another possibility for processes to interact are resources. They will be
discussed in the next&amp;nbsp;guide.&lt;/p&gt;
&lt;section id="sleep-until-woken-up-1"&gt;
&lt;span id="sleep-until-woken-up"&gt;&lt;/span&gt;&lt;h2&gt;Sleep until woken&amp;nbsp;up&lt;/h2&gt;
&lt;p&gt;Imagine you want to model an electric vehicle with an intelligent
battery-charging controller. While the vehicle is driving, the controller can
be passive but needs to be reactivate once the vehicle is connected to the
power grid in order to charge the&amp;nbsp;battery.&lt;/p&gt;
&lt;p&gt;In SimPy 2, this pattern was known as &lt;em&gt;passivate / reactivate&lt;/em&gt;. In SimPy 3,
you can accomplish that with a simple, shared &lt;code&gt;Event&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;randint&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl_reactivate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drive&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="c1"&gt;# Drive for 20-40 min&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="c1"&gt;# Park for 1–6 hours&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Start parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl_reactivate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;quot;reactivate&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl_reactivate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Stop parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bat_ctrl&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. passivating at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl_reactivate&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;quot;passivate&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. reactivated at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="c1"&gt;# Intelligent charging behavior here …&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Bat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;passivating&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;
&lt;span class="n"&gt;Bat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;reactivated&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;
&lt;span class="n"&gt;Bat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;passivating&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="n"&gt;Stop&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since &lt;code&gt;bat_ctrl()&lt;/code&gt; just waits for a normal event, we no longer call this
pattern &lt;em&gt;passivate / reactivate&lt;/em&gt; in SimPy&amp;nbsp;3.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="waiting-for-another-process-to-terminate-1"&gt;
&lt;span id="waiting-for-another-process-to-terminate"&gt;&lt;/span&gt;&lt;h2&gt;Waiting for another process to&amp;nbsp;terminate&lt;/h2&gt;
&lt;p&gt;The example above has a problem: it may happen that the vehicles wants to park
for a shorter duration than it takes to charge the battery (this is the case if
both, charging and parking would take 60 to 90&amp;nbsp;minutes).&lt;/p&gt;
&lt;p&gt;To fix this problem we have to slightly change our model. A new &lt;code&gt;bat_ctrl()&lt;/code&gt;
will be started every time the &lt;span class="caps"&gt;EV&lt;/span&gt; starts parking. The &lt;span class="caps"&gt;EV&lt;/span&gt; then waits until the
parking duration is over &lt;em&gt;and&lt;/em&gt; until the charging has&amp;nbsp;stopped:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drive&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="c1"&gt;# Drive for 20-40 min&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="c1"&gt;# Park for 1–6 hours&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Start parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;charging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;charging&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Stop parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bat_ctrl&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. started at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="c1"&gt;# Intelligent charging behavior here …&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. done at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;310&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Start&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;
&lt;span class="n"&gt;Bat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;started&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;
&lt;span class="n"&gt;Bat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;ctrl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;
&lt;span class="n"&gt;Stop&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;305&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Again, nothing new (if you’ve read the &lt;a href="/2014/04/05/simpy-events/"&gt;events guide&lt;/a&gt;) and special is happening. SimPy processes are
events, too, so you can yield them and will thus wait for them to get
triggered. You can also wait for two events at the same time by concatenating
them with &lt;code&gt;&amp;amp;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="interrupting-another-process-1"&gt;
&lt;span id="interrupting-another-process"&gt;&lt;/span&gt;&lt;h2&gt;Interrupting another&amp;nbsp;process&lt;/h2&gt;
&lt;p&gt;As usual, we now have another problem: Imagine, a trip is very urgent, but with
the current implementation, we always need to wait until the battery is fully
charged. If we could somehow interrupt that&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;Fortunate coincidence, there is indeed a way to do exactly this. You can call
&lt;code&gt;interrupt()&lt;/code&gt; on a &lt;code&gt;Process&lt;/code&gt;. This will throw an &lt;code&gt;Interrupt&lt;/code&gt; exception
into that process, resuming it&amp;nbsp;immediately:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;drive&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="c1"&gt;# Drive for 20-40 min&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="c1"&gt;# Park for 1 hour&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Start parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="n"&gt;charging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bat_ctrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="n"&gt;parking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;charging&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;parking&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;charging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                &lt;span class="c1"&gt;# Interrupt charging if not already done.&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                &lt;span class="n"&gt;charging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Need to go!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Stop parking at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bat_ctrl&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. started at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. done at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="c1"&gt;# Onoes! Got interrupted before the charging was done.&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bat. ctrl. interrupted at&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;msg:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                  &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;Start parking at 31&lt;/span&gt;
&lt;span class="go"&gt;Bat. ctrl. started at 31&lt;/span&gt;
&lt;span class="go"&gt;Stop parking at 91&lt;/span&gt;
&lt;span class="go"&gt;Bat. ctrl. interrupted at 91 msg: Need to go!&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What &lt;code&gt;process.interrupt()&lt;/code&gt; actually does is removing the process’
&lt;code&gt;_resume()&lt;/code&gt; method from the callbacks of the event that it is currently
waiting for. And it will schedule an event that will throw the &lt;code&gt;Interrupt&lt;/code&gt;
exception into the interrupted process as soon as&amp;nbsp;possible.&lt;/p&gt;
&lt;p&gt;Since we don’t to anything special to the event, the interrupted process can
yield the same event again after catching the &lt;code&gt;Interrupt&lt;/code&gt; – Imagine someone
waiting for a shop to open. The person may get interrupted by a phone call.
After finishing the call, he or she checks if the shop already opened and
either enters or continues to&amp;nbsp;wait.&lt;/p&gt;
&lt;p&gt;This guide is now, of course, also part of the &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/process_interaction.html"&gt;SimPy documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2014"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>SimPy: Events</title><link href="https://stefan.sofa-rockers.org/2014/04/05/simpy-events/" rel="alternate"></link><published>2014-04-05T11:52:00+02:00</published><updated>2014-04-05T11:52:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-04-05:/2014/04/05/simpy-events/</id><summary type="html">&lt;p&gt;SimPy is a discrete-event simulation library for Python. This guide
describes how events in SimPy work and which types of events SimPy&amp;nbsp;offers.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It’s been &lt;a href="/2014/01/20/simpy-environments/"&gt;a while&lt;/a&gt; since the last SimPy
guide, but I’ve been quite busy with developing and Open Sourcing &lt;a href="https://mosaik.offis.de/2014/03/27/mosaik-is-open-source/"&gt;mosaik
2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“What’s SimPy again?”&lt;/em&gt; &lt;a href="https://simpy.readthedocs.org"&gt;SimPy&lt;/a&gt; is
a process-based discrete-event simulation framework based on standard Python.
Its event dispatcher is based on Python’s generators and can also be used for
&lt;a href="https://bitbucket.org/simpy/simpy.io/"&gt;asynchronous networking&lt;/a&gt; or to
implement multi-agent systems (with both, simulated and real&amp;nbsp;communication).&lt;/p&gt;
&lt;p&gt;The first to guides provided a &lt;a href="/2013/12/03/how-simpy-works/"&gt;general description of how SimPy works&lt;/a&gt; and an &lt;a href="/2014/01/20/simpy-environments/"&gt;explanation of SimPy environments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This guide is all about&amp;nbsp;events.&lt;/p&gt;
&lt;p&gt;SimPy includes an extensive set of event types for various purposes. All of
them inherit &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event"&gt;simpy.events.Event&lt;/a&gt;.
The listing below shows the hierarchy of events built into&amp;nbsp;SimPy:&lt;/p&gt;
&lt;pre&gt;events.Event
↑
+— events.Timeout
|
+— events.Initialize
|
+— events.Process
|
+— events.Condition
|  ↑
|  +— events.AllOf
|  |
|  +— events.AnyOf
⋮
+– [resource events]&lt;/pre&gt;
&lt;p&gt;This is the set of basic events. Events are extensible and resources, for
example, define additional events. In this guide, we’ll focus on the events in
the &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#module-simpy.events"&gt;simpy.events&lt;/a&gt;&amp;nbsp;module.&lt;/p&gt;
&lt;section id="event-basics"&gt;
&lt;h2&gt;Event&amp;nbsp;basics&lt;/h2&gt;
&lt;p&gt;SimPy events are very similar – if not identical — to deferreds, futures or
promises. Instances of the class &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event"&gt;Event&lt;/a&gt;
are used to describe any kind of events. Events can be in one of the following
states. An&amp;nbsp;event&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;might happen (not&amp;nbsp;triggered),&lt;/li&gt;
&lt;li&gt;is going to happen (triggered)&amp;nbsp;or&lt;/li&gt;
&lt;li&gt;has happened&amp;nbsp;(processed).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They traverse these states exactly once in that order. Events are also tightly
bound to time and time causes events to advance their&amp;nbsp;state.&lt;/p&gt;
&lt;p&gt;Initially, events are not triggered and just objects in&amp;nbsp;memory.&lt;/p&gt;
&lt;p&gt;If an event gets triggered, it is scheduled at a given time and inserted into
SimPy’s event queue. The property &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event.triggered"&gt;Event.triggered&lt;/a&gt;
becomes &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As long as the event is not &lt;em&gt;processed&lt;/em&gt;, you can add &lt;em&gt;callbacks&lt;/em&gt; to an event.
Callbacks are callables that accept an event as parameter and are stored in the
&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event.callbacks"&gt;Event.callbacks&lt;/a&gt;&amp;nbsp;list.&lt;/p&gt;
&lt;p&gt;An event becomes &lt;em&gt;processed&lt;/em&gt; when SimPy pops it from the event queue and calls
all of its callbacks. It is now no longer possible to add callbacks. The
property &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event.processed"&gt;Event.processed&lt;/a&gt;
becomes &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Events also have a &lt;em&gt;value&lt;/em&gt;. The value can be set before or when the event is
triggered and can be retrieved via &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event.value"&gt;Event.value&lt;/a&gt;
or, within a process, by yielding the event (&lt;code&gt;value = yield event&lt;/code&gt;).&lt;/p&gt;
&lt;section id="adding-callbacks-to-an-event"&gt;
&lt;h3&gt;Adding callbacks to an&amp;nbsp;event&lt;/h3&gt;
&lt;p&gt;“What? Callbacks? I’ve never seen no callbacks!”, you might think if you have
worked your way through the &lt;a href="http://simpy.readthedocs.org/en/latest/simpy_intro/index.html"&gt;tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That’s on purpose. The most common way to add a callback to an event is
yielding it from your process function (&lt;code&gt;yield event&lt;/code&gt;). This will add the
process’ &lt;em&gt;_resume()&lt;/em&gt; method as a callback. That’s how your process gets resumed
when it yielded an&amp;nbsp;event.&lt;/p&gt;
&lt;p&gt;However, you can add any callable object (function) to the list of callbacks
as long as it accepts an event instance as its single&amp;nbsp;parameter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Called back from&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callbacks&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;my_callback&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If an event has been &lt;em&gt;processed&lt;/em&gt;, all of its &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event.callbacks"&gt;Event.callbacks&lt;/a&gt;
have been executed and the attribute is set to &lt;code&gt;None&lt;/code&gt;. This is to prevent you
from adding more callbacks – these would of course never get called because the
event has already&amp;nbsp;happened.&lt;/p&gt;
&lt;p&gt;Processes are smart about this, though. If you yield a processed event,
&lt;em&gt;_resume()&lt;/em&gt; will immediately resume your process with the value of the event
(because there is nothing to wait&amp;nbsp;for).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="triggering-events"&gt;
&lt;h3&gt;Triggering&amp;nbsp;events&lt;/h3&gt;
&lt;p&gt;When events are triggered, they can either &lt;em&gt;succeed&lt;/em&gt; or &lt;em&gt;fail&lt;/em&gt;. For example, if
an event is to be triggered at the end of a computation and everything works
out fine, the event will &lt;em&gt;succeed&lt;/em&gt;. If an exceptions occurs during that
computation, the event will &lt;em&gt;fail&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To trigger an event and mark it as successful, you can use
&lt;code&gt;Event.succeed(value=None)&lt;/code&gt;. You can optionally pass a &lt;em&gt;value&lt;/em&gt; to it (e.g.,
the results of a&amp;nbsp;computation).&lt;/p&gt;
&lt;p&gt;To trigger an event and mark it as failed, call &lt;code&gt;Event.fail(exception)&lt;/code&gt;
and pass an &lt;code&gt;Exception&lt;/code&gt; instance to it (e.g., the exception you caught
during your failed&amp;nbsp;computation).&lt;/p&gt;
&lt;p&gt;There is also a generic way to trigger an event: &lt;code&gt;Event.trigger(event)&lt;/code&gt;.
This will take the value and outcome (success or failure) of the event passed
to&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;All three methods return the event instance they are bound to. This allows you
to do things like &lt;code&gt;yield Event(env).succeed()&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="example-usages-for-event"&gt;
&lt;h2&gt;Example usages for &lt;code&gt;Event&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The simple mechanics outlined above provide a great flexibility in the way
events (even the basic &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event"&gt;Event&lt;/a&gt;)
can be&amp;nbsp;used.&lt;/p&gt;
&lt;p&gt;One example for this is that events can be shared. They can be created by a
process or outside of the context of a process. They can be passed to other
processes and&amp;nbsp;chained:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;School&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_ends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pupil_procs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pupil&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bell_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bell&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bell&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="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_ends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_ends&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pupil&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="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; \o/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_ends&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;school&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;School&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
 \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; \&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This can also be used like the &lt;em&gt;passivate / reactivate&lt;/em&gt; known from SimPy 2.
The pupils &lt;em&gt;passivate&lt;/em&gt; when class begins and are &lt;em&gt;reactivated&lt;/em&gt; when the bell&amp;nbsp;rings.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="let-time-pass-by-the-timeout"&gt;
&lt;h2&gt;Let time pass by: the &lt;code&gt;Timeout&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To actually let time pass in a simulation, there is the &lt;em&gt;timeout&lt;/em&gt; event.
A timeout has two parameters: a &lt;em&gt;delay&lt;/em&gt; and an optional &lt;em&gt;value&lt;/em&gt;:
&lt;code&gt;Timeout(delay, value=None)&lt;/code&gt;. It triggers itself during its creation and
schedules itself at &lt;code&gt;now + delay&lt;/code&gt;. Thus, the &lt;code&gt;succeed()&lt;/code&gt; and &lt;code&gt;fail()&lt;/code&gt;
methods cannot be called again and you have to pass the event value to it when
you create the&amp;nbsp;timeout.&lt;/p&gt;
&lt;p&gt;The delay can be any kind of number, usually an &lt;em&gt;int&lt;/em&gt; or &lt;em&gt;float&lt;/em&gt; as long as it
supports comparison and&amp;nbsp;addition.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="processes-are-events-too"&gt;
&lt;h2&gt;Processes are events,&amp;nbsp;too&lt;/h2&gt;
&lt;p&gt;SimPy processes (as created by &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Process"&gt;Process&lt;/a&gt;
or &lt;code&gt;env.process()&lt;/code&gt;) have the nice property of being events,&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;That means, that a process can yield another process. It will then be resumed
when the other process ends. The event’s value will be the return value of that&amp;nbsp;process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The example above will only work in Python &amp;gt;= 3.3. As a workaround for older
Python versions, you can use &lt;code&gt;env.exit(23)&lt;/code&gt; with the same&amp;nbsp;effect.&lt;/p&gt;
&lt;p&gt;When a process is created, it schedules an &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Initialize"&gt;Initialize&lt;/a&gt;
event which will start the execution of the process when triggered. You usually
won’t have to deal with this type of&amp;nbsp;event.&lt;/p&gt;
&lt;p&gt;If you don’t want a process to start immediately but after a certain delay, you
can use &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.util.html#simpy.util.start_delayed"&gt;simpy.util.start_delayed()&lt;/a&gt;.
This method returns a helper process that uses a &lt;em&gt;timeout&lt;/em&gt; before actually
starting a&amp;nbsp;process.&lt;/p&gt;
&lt;p&gt;The example from above, but with a delayed start of &lt;code&gt;sub()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;simpy.util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;start_delayed&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;sub_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;start_delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;sub_proc&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="waiting-for-for-multiple-events-at-once"&gt;
&lt;h2&gt;Waiting for for multiple events at&amp;nbsp;once&lt;/h2&gt;
&lt;p&gt;Sometimes, you want to wait for more than one event at the same time. For
example, you may want to wait for a resource, but not for an unlimited amount
of time. Or you may want to wait until all of a number of events
have&amp;nbsp;happened.&lt;/p&gt;
&lt;p&gt;SimPy therefore offers the &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.AnyOf"&gt;AnyOf&lt;/a&gt;
and &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.AllOf"&gt;AllOf&lt;/a&gt;
events which both are a &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Condition"&gt;Condition&lt;/a&gt;&amp;nbsp;event.&lt;/p&gt;
&lt;p&gt;Both take a list of events as an argument and are triggered if at least one
of them is triggered or all of&amp;nbsp;them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;simpy.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AnyOf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllOf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnyOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Triggers if at least one of &amp;quot;events&amp;quot; is triggered.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AllOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Triggers if all each of &amp;quot;events&amp;quot; is triggered.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The value of a condition event is a dictionary with an entry for every
triggered event. In the case of &lt;code&gt;AllOf&lt;/code&gt;, the size of that dictionary will
always be the same as the length of the event list. The value dict of &lt;code&gt;AnyOf&lt;/code&gt;
will have at least one entry. In both cases, the event instances are used as
keys and the event values will be the&amp;nbsp;values.&lt;/p&gt;
&lt;p&gt;As a shorthand for &lt;code&gt;AllOf&lt;/code&gt; and &lt;code&gt;AnyOf&lt;/code&gt;, you can also use the logical
operators &lt;code&gt;&amp;amp;&lt;/code&gt; (and) and &lt;code&gt;|&lt;/code&gt; (or):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;eggs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;eggs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;eggs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="c1"&gt;# You can also concatenate &amp;amp; and |&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;triggered&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s it. I’ve already started with the next guide – about process interaction
– so hopefully it won’t take to long until publish it this&amp;nbsp;time.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2014"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>mosaik – An open co-simulation framework for smart energy systems</title><link href="https://stefan.sofa-rockers.org/2014/03/27/mosaik/" rel="alternate"></link><published>2014-03-27T13:37:00+01:00</published><updated>2014-03-27T13:37:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-03-27:/2014/03/27/mosaik/</id><summary type="html">&lt;p&gt;Mosaik is a co-simulation framework for smart energy systems. We’ve
been working on it since four years now and finally had the
opportunity to release it as Open Source&amp;nbsp;Software.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Since nearly four years, we’ve been developing and working with &lt;a href="http://mosaik.offis.de"&gt;mosaik&lt;/a&gt; within our research group at &lt;span class="caps"&gt;OFFIS&lt;/span&gt;. Now, we finally
had the opportunity to release it as Open Source&amp;nbsp;Software.&lt;/p&gt;
&lt;p&gt;You can read the full announcement &lt;a href="https://mosaik.offis.de/2014/03/27/mosaik-is-open-source/"&gt;here&lt;/a&gt;.&lt;/p&gt;
</content><category term="2014"></category><category term="python"></category><category term="simulation"></category><category term="mosaik"></category></entry><entry><title>SimPy: Environments</title><link href="https://stefan.sofa-rockers.org/2014/01/20/simpy-environments/" rel="alternate"></link><published>2014-01-20T17:33:00+01:00</published><updated>2014-01-20T17:33:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2014-01-20:/2014/01/20/simpy-environments/</id><summary type="html">&lt;p&gt;SimPy is a discrete-event simulation library for Python. This guide
describes the simulation environments: A simulation environment
manages the simulation time as well as the scheduling and processing
of events. It also provides means to step through or execute the&amp;nbsp;simulation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This is the second in a &lt;a href="/2013/12/03/how-simpy-works/"&gt;series&lt;/a&gt; of guides that describe how SimPy works and
how to use it best. This time I’ll discuss &lt;em&gt;environments&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A simulation environment manages the simulation time as well as the scheduling
and processing of events. It also provides means to step through or execute the&amp;nbsp;simulation.&lt;/p&gt;
&lt;p&gt;The base class for all environments is &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.BaseEnvironment"&gt;BaseEnvironment&lt;/a&gt;. “Normal” simulations
usually use its subclass &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment"&gt;Environment&lt;/a&gt;. For real-time simulations, SimPy
provides a &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.rt.html#simpy.rt.RealtimeEnvironment"&gt;RealtimeEnvironment&lt;/a&gt; (more on that in another&amp;nbsp;guide).&lt;/p&gt;
&lt;section id="simulation-control"&gt;
&lt;h2&gt;Simulation&amp;nbsp;control&lt;/h2&gt;
&lt;p&gt;SimPy is very flexible in terms of simulation execution. You can run your
simulation until there is no more event, until a certain simulation time is
reached, or until a certain event is triggered. You can also step through the
simulation event by event. Furthermore, you can mix these things as you&amp;nbsp;like.&lt;/p&gt;
&lt;p&gt;For example, you could run your simulation until an interesting event occurs.
You could then step through the simulation event by event for a while; and
finally run the simulation until there is no more event left and your processes
all have&amp;nbsp;terminated.&lt;/p&gt;
&lt;p&gt;The most important method here is &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.run"&gt;Environment.run()&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you call it without any argument (&lt;code&gt;env.run()&lt;/code&gt;), it steps through the
simulation until there is no more event&amp;nbsp;left.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span class="caps"&gt;WARNING&lt;/span&gt;:&lt;/em&gt; If your processes run forever (&lt;code&gt;while True: yield
env.timeout(1)&lt;/code&gt;), this method will never terminate (unless you kill your
script by e.g., pressing &lt;code&gt;Ctrl-C&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In most cases it is more advisable to stop your simulation when it reaches
a certain simulation time. Therefore, you can pass the desired time via the
&lt;em&gt;until&lt;/em&gt; parameter, e.g.: &lt;code&gt;env.run(until=10)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The simulation will then stop when the internal clock reaches 10 but will not
process any events scheduled for time 10. This is similar to a new
environment where the clock is 0 but (obviously) no events have yet been&amp;nbsp;processed.&lt;/p&gt;
&lt;p&gt;If you want to integrate your simulation in a &lt;span class="caps"&gt;GUI&lt;/span&gt; and want to draw a
process bar, you can repeatedly call this function with increasing &lt;em&gt;until&lt;/em&gt;
values and update your progress bar after each&amp;nbsp;call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;progressbar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instead of passing a number to &lt;code&gt;run()&lt;/code&gt;, you can also pass any event to it.
&lt;code&gt;run()&lt;/code&gt; will then return when the event has been&amp;nbsp;processed.&lt;/p&gt;
&lt;p&gt;Assuming that the current time is 0, &lt;code&gt;env.run(until=env.timeout(5))&lt;/code&gt; is
equivalent to &lt;code&gt;env.run(until=5)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also pass other types of events (remember, that a &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Process"&gt;Process&lt;/a&gt; is an
event,&amp;nbsp;too):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Monty Python’s Flying Circus&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;&amp;#39;Monty Python’s Flying Circus&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To step through the simulation event by event, the environment offers
&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.peek"&gt;peek()&lt;/a&gt; and &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.step"&gt;step()&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;peek()&lt;/code&gt; returns the time of the next scheduled event of &lt;em&gt;infinity&lt;/em&gt;
(&lt;code&gt;float('inf')&lt;/code&gt;) of no more event is&amp;nbsp;scheduled.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;step()&lt;/code&gt; processes the next scheduled event. It raises an &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.EmptySchedule"&gt;EmptySchedule&lt;/a&gt;
exception if no event is&amp;nbsp;available.&lt;/p&gt;
&lt;p&gt;In a typical use case, you use these methods in a loop&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;until&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peek&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
   &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="state-access"&gt;
&lt;h2&gt;State&amp;nbsp;access&lt;/h2&gt;
&lt;p&gt;The environment allows you to get the current simulation time via the
&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.now"&gt;Environment.now&lt;/a&gt; property. The simulation time is a number without unit
and is increased via &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Timeout"&gt;Timeout&lt;/a&gt;&amp;nbsp;events.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;now&lt;/code&gt; starts at 0, but you can pass an &lt;code&gt;initial_time&lt;/code&gt; to the
Environment to use something&amp;nbsp;else.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;span class="caps"&gt;NOTE&lt;/span&gt;:&lt;/em&gt; Although the simulation time is technically unitless, you can pretend
that it is, for example, in seconds and use it like a timestamp returned by
&lt;a href="http://docs.python.org/3/library/time#time.time"&gt;time.time()&lt;/a&gt; to calculate a date or the day of the&amp;nbsp;week.&lt;/p&gt;
&lt;p&gt;The property &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.active_process"&gt;Environment.active_process&lt;/a&gt; is comparable to &lt;a href="http://docs.python.org/3/library/os#os.getpid"&gt;os.getpid()&lt;/a&gt;
and is either &lt;code&gt;None&lt;/code&gt; or pointing at the currently active Process. A process
is &lt;em&gt;active&lt;/em&gt; when its process function is being executed. It becomes &lt;em&gt;inactive&lt;/em&gt;
(or suspended) when it yields an&amp;nbsp;event.&lt;/p&gt;
&lt;p&gt;Thus, it makes only sense to access this property from within a process
function or a function that is called by your process&amp;nbsp;function:&lt;/p&gt;
&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; def subfunc(env):
...     print(env.active_process)  # will print &amp;quot;p1&amp;quot;
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; def my_proc(env):
...     while True:
...         print(env.active_process)  # will print &amp;quot;p1&amp;quot;
...         subfunc(env)
...         yield env.timeout(1)
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; env = simpy.Environment()
&amp;gt;&amp;gt;&amp;gt; p1 = env.process(my_proc(env))
&amp;gt;&amp;gt;&amp;gt; env.active_process  # None
&amp;gt;&amp;gt;&amp;gt; env.step()
&amp;lt;Process(my_proc) object at 0x...&amp;gt;
&amp;lt;Process(my_proc) object at 0x...&amp;gt;
&amp;gt;&amp;gt;&amp;gt; env.active_process  # None&lt;/pre&gt;
&lt;p&gt;An exemplary use case for this is the resource system: If a process function
calls &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.resources.resource.html#simpy.resources.resource.Resource.request"&gt;request()&lt;/a&gt; to request
a resource, the resource determines the requesting process via
&lt;code&gt;env.active_process&lt;/code&gt;. Take a &lt;a href="https://bitbucket.org/simpy/simpy/src/3.0.2/simpy/resources/base.py#cl-35"&gt;look at the code&lt;/a&gt; to see how we do this&amp;nbsp;:-).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="event-creation"&gt;
&lt;h2&gt;Event&amp;nbsp;creation&lt;/h2&gt;
&lt;p&gt;To create events, you normally have to import &lt;code&gt;simpy.events&lt;/code&gt;, instantiate the
event class and pass a reference to the environment to it. To reduce the amount
of typing, the Environment provides some shortcuts for event creation. For
example, &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.event"&gt;Environment.event()&lt;/a&gt; is equivalent to &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event"&gt;simpy.events.Event(env)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Other shortcuts&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.process"&gt;Environment.process()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.timeout"&gt;Environment.timeout()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.all_of"&gt;Environment.all_of()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.any_of"&gt;Environment.any_of()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More details on what the events do can be found in the guide to events (not yet
written&amp;nbsp;:-)).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="miscellaneous"&gt;
&lt;h2&gt;Miscellaneous&lt;/h2&gt;
&lt;p&gt;Since Python 3.3, a generator function can have a return&amp;nbsp;value:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In SimPy, this can be used to provide return values for processes that can be
used by other&amp;nbsp;processes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;other_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ret_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ret_val&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Internally, Python passes the return value as parameter to the
&lt;code&gt;StopIteration&lt;/code&gt; exception that it raises when a generator is exhausted. So
in Python 2.7 and 3.2 you could replace the &lt;code&gt;return 42&lt;/code&gt; with a &lt;code&gt;raise
StopIteration(42)&lt;/code&gt; to achieve the same&amp;nbsp;result.&lt;/p&gt;
&lt;p&gt;To keep your code more readable, the environment provides the method &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.exit"&gt;exit()&lt;/a&gt;
to do exactly&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Py2 equivalent to &amp;quot;return 42&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find the complete guide on &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/environments.html"&gt;Read the Docs&lt;/a&gt;. The next one will be
about events and the event types provided by&amp;nbsp;SimPy.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2014"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>How SimPy works</title><link href="https://stefan.sofa-rockers.org/2013/12/03/how-simpy-works/" rel="alternate"></link><published>2013-12-03T09:48:00+01:00</published><updated>2013-12-03T09:48:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2013-12-03:/2013/12/03/how-simpy-works/</id><summary type="html">&lt;p&gt;SimPy is a discrete-event simulation library for Python. This guide
describes the basic concepts of SimPy: How does it work? What are
processes, events and the environment? What can I do with&amp;nbsp;them?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;SimPy is a discrete event-simulation library for Python. Recently, the new
version 3 has ben &lt;a href="/2013/10/11/simpy-3-released/"&gt;released&lt;/a&gt;. With that new release, we also changed the
structure of the documentation. It now features a &lt;a href="http://simpy.readthedocs.org/en/latest/simpy_intro/"&gt;Tutorial&lt;/a&gt;, &lt;a href="http://simpy.readthedocs.org/en/latest/topical_guides/"&gt;Topical
Guides&lt;/a&gt;, an &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/"&gt;&lt;span class="caps"&gt;API&lt;/span&gt; Reference&lt;/a&gt; and a list of &lt;a href="http://simpy.readthedocs.org/en/latest/examples/"&gt;examples&lt;/a&gt;. Most of the topical
guides have yet to written, though. So this is the first in a series of posts
describing various concepts of&amp;nbsp;SimPy.&lt;/p&gt;
&lt;p&gt;If you break SimPy down, it is just an asynchronous event dispatcher. You
generate events and schedule them at a given simulation time. Events are sorted
by priority, simulation time, and an increasing event id. An event also has
a list of callbacks, which are executed when the event is triggered and
processed by the event loop. Events may also have a return&amp;nbsp;value.&lt;/p&gt;
&lt;p&gt;The components involved in this are the &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment"&gt;Environment&lt;/a&gt;, &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#module-simpy.events"&gt;events&lt;/a&gt; and the process
functions that you&amp;nbsp;write.&lt;/p&gt;
&lt;p&gt;Process functions implement your simulation model, that is, they define the
behavior of your simulation. They are plain Python generator functions that
yield instances of &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Event"&gt;Event&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The environment stores these events in its event list and keeps track of the
current simulation&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;If a process function yields and event, SimPy adds the process to the event’s
callbacks and suspends the process until the event is triggered and processed.
When a process waiting for an event is resumed, it will also receive the
event’s&amp;nbsp;value.&lt;/p&gt;
&lt;p&gt;Here is a very simple example that illustrates all this; the code is more
verbose than it needs to be to make things extra clear. You find a compact
version of it at the end of this&amp;nbsp;section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delay&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="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;now=&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, value=&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;example_gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;example_gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;now=1, value=42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;example()&lt;/code&gt; process function above first creates a &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Timeout"&gt;Timeout&lt;/a&gt; event. It
passes the environment, a delay, and a value to it. The Timeout schedules
itself at &lt;code&gt;now + delay&lt;/code&gt; (that’s why the environment is required); other event
types usually schedule themselves at the current simulation&amp;nbsp;time.&lt;/p&gt;
&lt;p&gt;The process function then yields the event and thus gets suspended. It is
resumed, when SimPy processes the Timeout event. The process function also
receives the event’s value (42) – this is, however, optional, so &lt;code&gt;yield
event&lt;/code&gt; would have been okay if the you were not interested in the value or if
the event had no value at&amp;nbsp;all.&lt;/p&gt;
&lt;p&gt;Finally, the process function prints the current simulation time (that is
accessible via the environment’s &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment.now"&gt;now&lt;/a&gt; attribute) and the Timeout’s&amp;nbsp;value.&lt;/p&gt;
&lt;p&gt;If all required process functions are defined, you can instantiate all objects
for your simulation. In most cases, you start by creating an instance of
&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.core.html#simpy.core.Environment"&gt;Environment&lt;/a&gt;, because you’ll need to pass it around a lot
when creating everything&amp;nbsp;else.&lt;/p&gt;
&lt;p&gt;Starting a process function involves two&amp;nbsp;things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have to call the process function to create a generator object. (This
will not execute any code of that function yet. Please read &lt;a href="http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained/231855#231855"&gt;The Python
yield keyword explained&lt;/a&gt;, to understand why this is the&amp;nbsp;case.)&lt;/li&gt;
&lt;li&gt;You then create an instance of &lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Process"&gt;Process&lt;/a&gt; and pass the
environment and the generator object to it. This will schedule an
&lt;a href="http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#simpy.events.Initialize"&gt;Initialize&lt;/a&gt; event at the current simulation time which
starts the execution of the process function. The process instance is also
an event that is triggered when the process function returns. The
guide to events &lt;em&gt;(not yet written)&lt;/em&gt; explains why this is&amp;nbsp;handy.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, you can start SimPy’s event loop. By default, it will run as long as
there are events in the event list, but you can also let it stop earlier by
providing an &lt;code&gt;until&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;The next guide/post will describe the environment and its interactions with
events and process functions in more&amp;nbsp;detail.&lt;/p&gt;
&lt;section id="best-practice-version-of-the-example-above"&gt;
&lt;h2&gt;“Best practice” version of the example&amp;nbsp;above&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;now=&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, value=&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="go"&gt;now=1, value=42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
</content><category term="2013"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>SimPy 3.0.2 released</title><link href="https://stefan.sofa-rockers.org/2013/10/24/simpy-302-released/" rel="alternate"></link><published>2013-10-24T22:37:00+02:00</published><updated>2013-10-24T22:37:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2013-10-24:/2013/10/24/simpy-302-released/</id><summary type="html">&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/simpy/3.0.2"&gt;SimPy 3.0.2&lt;/a&gt; has just
been released. It fixes the default capacity for &lt;code&gt;Container&lt;/code&gt;,
&lt;code&gt;Store&lt;/code&gt; and &lt;code&gt;FilterStore&lt;/code&gt;, which is now&amp;nbsp;unlimited.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://pypi.python.org/pypi/simpy/3.0.2"&gt;SimPy 3.0.2&lt;/a&gt; has just been
released. It fixes the default capacity for &lt;code&gt;Container&lt;/code&gt;, &lt;code&gt;Store&lt;/code&gt; and
&lt;code&gt;FilterStore&lt;/code&gt;, which is now&amp;nbsp;unlimited.&lt;/p&gt;
&lt;section id="what-is-simpy"&gt;
&lt;h2&gt;What is&amp;nbsp;SimPy?&lt;/h2&gt;
&lt;p&gt;SimPy is a process-based discrete-event simulation framework based on standard
Python. Its event dispatcher is based on Python’s generators and can also be
used for asynchronous networking or to implement multi-agent systems (with
both, simulated and real&amp;nbsp;communication).&lt;/p&gt;
&lt;p&gt;SimPy 3 has been rewritten from scratch. This is the &lt;a href="http://stefan.sofa-rockers.org/2013/10/11/simpy-3-released/"&gt;original release&lt;/a&gt;&amp;nbsp;announcement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After one and a half year of development and many iterations, prototypes,
and endless discussions, we now proudly release SimPy&amp;nbsp;3.&lt;/p&gt;
&lt;p&gt;SimPy 3 has been completely rewritten from scratch. Our main goals were to
simplify the &lt;span class="caps"&gt;API&lt;/span&gt; and code base as well as making SimPy more flexible and
extensible. Some of the most important changes&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stronger focus on events. Processes yield event instances and are
suspended until the event is triggered. An example for an event is
a &lt;em&gt;timeout&lt;/em&gt; (formerly known as &lt;em&gt;hold&lt;/em&gt;), but even processes are now
events, too (you can wait until a process&amp;nbsp;terminates).&lt;/li&gt;
&lt;li&gt;Events can be combined with &lt;code&gt;&amp;amp;&lt;/code&gt; (and) and &lt;code&gt;|&lt;/code&gt; (or) to create
&lt;em&gt;condition events&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Process can now be defined by any generator function. You don’t have to
subclass &lt;code&gt;Process&lt;/code&gt; anymore.&lt;/li&gt;
&lt;li&gt;No more global simulation state. Every simulation stores its state in an
&lt;em&gt;environment&lt;/em&gt; which is comparable to the old &lt;code&gt;Simulation&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Improved resource system with newly added resource&amp;nbsp;types.&lt;/li&gt;
&lt;li&gt;Removed plotting and &lt;span class="caps"&gt;GUI&lt;/span&gt; capabilities. &lt;a href="http://qt-project.org/wiki/PySide"&gt;Pyside&lt;/a&gt; and &lt;a href="http://matplotlib.org/"&gt;matplotlib&lt;/a&gt; are
much better with&amp;nbsp;this.&lt;/li&gt;
&lt;li&gt;Greatly improved test suite. Its cleaner, and the tests are shorter and
more&amp;nbsp;numerous.&lt;/li&gt;
&lt;li&gt;Completely overhauled&amp;nbsp;documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a &lt;a href="https://simpy.readthedocs.org/en/latest/about/history.html"&gt;guide for porting from SimPy 2 to SimPy 3&lt;/a&gt;. If you want to
stick to SimPy 2 for a while, change your requirements to
&lt;code&gt;'SimPy&amp;gt;=2.3,&amp;lt;3'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All in all, SimPy has become a framework for asynchronous programming based
on coroutines. It brings more than ten years of experience and scientific
know-how in the field of event-discrete simulation to the world of
asynchronous programming and should thus be a solid foundation for
everything based on an event&amp;nbsp;loop.&lt;/p&gt;
&lt;p&gt;SimPy 3 is dedicated to &lt;em&gt;Klaus G. Müller&lt;/em&gt; and &lt;em&gt;Tony Vignaux&lt;/em&gt; who originally
created SimPy in 2002. Without their hard work and efforts to create and
document SimPy and to build a friendly community around it, we wouldn’t be
where we are today. Thank&amp;nbsp;you!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/section&gt;
</content><category term="2013"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>SimPy 3 released</title><link href="https://stefan.sofa-rockers.org/2013/10/11/simpy-3-released/" rel="alternate"></link><published>2013-10-11T22:35:00+02:00</published><updated>2013-10-11T22:35:00+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2013-10-11:/2013/10/11/simpy-3-released/</id><summary type="html">&lt;p&gt;&lt;a href="http://simpy.rtfd.org"&gt;SimPy 3&lt;/a&gt; has finally been released. It has
been rewritten completely from scratch and is now easier to use and
more flexible than ever&amp;nbsp;before.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;After one and a half year of development and many iterations, prototypes, and
endless discussions, we now proudly release SimPy&amp;nbsp;3.&lt;/p&gt;
&lt;p&gt;SimPy 3 has been completely rewritten from scratch. Our main goals were to
simplify the &lt;span class="caps"&gt;API&lt;/span&gt; and code base as well as making SimPy more flexible and
extensible. Some of the most important changes&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stronger focus on events. Processes yield event instances and are suspended
until the event is triggered. An example for an event is a &lt;em&gt;timeout&lt;/em&gt;
(formerly known as &lt;em&gt;hold&lt;/em&gt;), but even processes are now events, too (you can
wait until a process&amp;nbsp;terminates).&lt;/li&gt;
&lt;li&gt;Events can be combined with &lt;code&gt;&amp;amp;&lt;/code&gt; (and) and &lt;code&gt;|&lt;/code&gt; (or) to create
&lt;em&gt;condition events&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Process can now be defined by any generator function. You don’t have to
subclass &lt;code&gt;Process&lt;/code&gt; anymore.&lt;/li&gt;
&lt;li&gt;No more global simulation state. Every simulation stores its state in an
&lt;em&gt;environment&lt;/em&gt; which is comparable to the old &lt;code&gt;Simulation&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Improved resource system with newly added resource&amp;nbsp;types.&lt;/li&gt;
&lt;li&gt;Removed plotting and &lt;span class="caps"&gt;GUI&lt;/span&gt; capabilities. &lt;a href="http://qt-project.org/wiki/PySide"&gt;Pyside&lt;/a&gt; and &lt;a href="http://matplotlib.org/"&gt;matplotlib&lt;/a&gt; are much
better with&amp;nbsp;this.&lt;/li&gt;
&lt;li&gt;Greatly improved test suite. Its cleaner, and the tests are shorter and more&amp;nbsp;numerous.&lt;/li&gt;
&lt;li&gt;Completely overhauled&amp;nbsp;documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a &lt;a href="https://simpy.readthedocs.org/en/latest/about/history.html"&gt;guide for porting from SimPy 2 to SimPy 3&lt;/a&gt;. If you want to stick
to SimPy 2 for a while, change your requirements to &lt;code&gt;'SimPy&amp;gt;=2.3,&amp;lt;3'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All in all, SimPy has become a framework for asynchronous programming based on
coroutines. It brings more than ten years of experience and scientific know-how
in the field of event-discrete simulation to the world of asynchronous
programming and should thus be a solid foundation for everything based on an
event&amp;nbsp;loop.&lt;/p&gt;
&lt;p&gt;SimPy 3 is dedicated to &lt;em&gt;Klaus G. Müller&lt;/em&gt; and &lt;em&gt;Tony Vignaux&lt;/em&gt; who originally
created SimPy in 2002. Without their hard work and efforts to create and
document SimPy and to build a friendly community around it, we wouldn’t be
where we are today. Thank&amp;nbsp;you!&lt;/p&gt;
</content><category term="2013"></category><category term="python"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>Handling sub-process hierarchies in Python on Linux, OS X and Windows</title><link href="https://stefan.sofa-rockers.org/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/" rel="alternate"></link><published>2013-08-15T12:52:34+02:00</published><updated>2013-08-15T12:52:34+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2013-08-15:/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/</id><summary type="html">&lt;p&gt;Windows doesn’t support Posix signals. I’ll show you how you can work
around this to cleanly terminate process&amp;nbsp;hierarchies.&lt;/p&gt;
</summary><content type="html">&lt;blockquote id="top"&gt;
&lt;span class="upperitalic"&gt;&lt;span class="caps"&gt;TL&lt;/span&gt;;&lt;span class="caps"&gt;DR&lt;/span&gt;&lt;/span&gt;—Windows doesn’t support Posix signals. I’ll show you
how you can work around this to cleanly terminate process hierarchies. Take
a look at the final &lt;a href="#example"&gt;example&lt;/a&gt;.&lt;/blockquote&gt;
&lt;p&gt;In this article, I’m trying solve an—at a first glance—simple&amp;nbsp;problem:&lt;/p&gt;
&lt;p&gt;You have a Python process that starts one ore more sub-processes, which again
might start their own sub-processes. If you press &lt;em&gt;Ctrl-C&lt;/em&gt; on the command line,
your main process and all of its sub-process should stop, maybe performing some
clean-up before they terminate. Also, one process may decide to stop one of its
children (including all of its&amp;nbsp;sub-processes).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Example:&lt;/em&gt; You have a server process that you start via the command line
and that starts several sub-processes. You usually stop that server and all
sub-processes by hitting &lt;em&gt;Ctrl-C&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You also want to start your server from within a test case to test its
replies on certain requests. If the test is done, you have to send some
kind of a message or signal to the server to stop it and all of its
sub-processes. The message should obviously be nothing that normal users
can send to the&amp;nbsp;server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This should not only work on Linux and &lt;span class="caps"&gt;OS&lt;/span&gt; X (which would be trivial), but also
on&amp;nbsp;Windows.&lt;/p&gt;
&lt;p&gt;Before I’ll show you some code, I’ll briefly describe what happens when you
press &lt;em&gt;Ctrl-C&lt;/em&gt; in a shell and which types of inter-process communication exist
to tell another process to stop. After that, I’ll introduce a simple example
which I’m then going to extend until the requirements stated above are&amp;nbsp;met.&lt;/p&gt;
&lt;p&gt;I’m using Python 3.3 for all examples. The code is tested on Ubuntu 13.04,
&lt;span class="caps"&gt;OS&lt;/span&gt; X 10.8 and Windows&amp;nbsp;7.&lt;/p&gt;
&lt;section id="signals-and-inter-process-communication"&gt;
&lt;span id="signals"&gt;&lt;/span&gt;&lt;h2&gt;Signals and inter-process&amp;nbsp;communication&lt;/h2&gt;
&lt;p&gt;Signals are a simple form of inter-process communication (&lt;span class="upper"&gt;&lt;span class="caps"&gt;IPC&lt;/span&gt;&lt;/span&gt;) on
Posix-compliant operating systems (like Linux or &lt;span class="caps"&gt;OS&lt;/span&gt; X). If one processes sends
a signal to another one, the &lt;span class="caps"&gt;OS&lt;/span&gt; interrupts its execution to deliver the signal.
The receiving process may now handle the&amp;nbsp;signal.&lt;/p&gt;
&lt;p&gt;Some interesting signals for this article are (quoted from &lt;a href="http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals"&gt;Wikipedia&lt;/a&gt;):&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;“The &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; signal is sent to a process by its controlling terminal
when a user wishes to interrupt the process. This is typically initiated by
pressing &lt;em&gt;Ctrl-C&lt;/em&gt;, but on some systems, the ‘delete’ character or ‘break’ key
can be&amp;nbsp;used.”&lt;/dd&gt;
&lt;dt&gt;&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;“The &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; signal is sent to a process to request its termination.
Unlike the &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGKILL&lt;/span&gt;&lt;/span&gt; signal, it can be caught and interpreted or
ignored by the process. This allows the process to perform nice termination
releasing resources and saving state if appropriate. It should be noted that
&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; is nearly identical to &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt;.”&lt;/dd&gt;
&lt;dt&gt;&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGKILL&lt;/span&gt;&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;“The &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGKILL&lt;/span&gt;&lt;/span&gt; signal is sent to a process to cause it to terminate
immediately. In contrast to &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; and &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;, this signal
cannot be caught or ignored, and the receiving process cannot perform any
clean-up upon receiving this&amp;nbsp;signal.”&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Though &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; and &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; are quite similar, the difference
between them is from my point of view that &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; is something
directly initiated by the user (you press &lt;em&gt;Ctrl-C&lt;/em&gt; in your terminal) while
&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; is rather used programmatically (e.g., processes receive
a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; when the &lt;span class="upper"&gt;&lt;span class="caps"&gt;OS&lt;/span&gt;&lt;/span&gt; is shutting&amp;nbsp;down).&lt;/p&gt;
&lt;p&gt;You can also group processes into &lt;a href="http://en.wikipedia.org/wiki/Process_group"&gt;process groups&lt;/a&gt; and send a signal to
all processes in that group at once. That’s for example what your terminal does
when you press &lt;em&gt;Ctrl-C&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If a Python program receives a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;, the default handler raises
a &lt;code&gt;KeyboardInterrupt&lt;/code&gt; by default. You can catch that exception and handle the
interrupt in anyway you want (e.g., terminate immediately, do some clean-up
before or just ignore it). Martin Cracauer &lt;a href="http://www.cons.org/cracauer/sigint.html"&gt;discusses&lt;/a&gt; how a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;
should be handled&amp;nbsp;properly.&lt;/p&gt;
&lt;p&gt;To handle a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; or suppress the &lt;code&gt;KeyboardInterrupt&lt;/code&gt; exception in
Python, you can register your own handler via &lt;code&gt;signal.signal()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since Windows is not Posix-compliant (what a shame), most of the signals won’t
work there, but we’ll come to this later. Tim Golden also &lt;a href="http://ramblings.timgolden.me.uk/2013/05/31/how-does-python-handle-signals-on-windows/"&gt;wrote&lt;/a&gt; about this.
Unix process groups also don’t work on&amp;nbsp;Windows.&lt;/p&gt;
&lt;p&gt;Other, more complicated (and more powerful) ways for &lt;span class="upper"&gt;&lt;span class="caps"&gt;IPC&lt;/span&gt;&lt;/span&gt; are, for
example, files, pipes and sockets. Using these, you can just send a message
like “plzdiekthxbye” to another process to tell it to&amp;nbsp;stop.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="stopping-processes-via-ctrl-c"&gt;
&lt;h2&gt;Stopping processes via &lt;em&gt;Ctrl-C&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;For all examples, we’ll use a simple process hierarchy: Process &lt;em&gt;A&lt;/em&gt; starts
process &lt;em&gt;B&lt;/em&gt; and process &lt;em&gt;B&lt;/em&gt; starts process &lt;em&gt;C&lt;/em&gt;. Every process will sleep for
ten seconds and waits for a &lt;code&gt;KeyboardInterrupt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&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;time&lt;/span&gt;


&lt;span class="n"&gt;PYTHON&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;
&lt;span class="n"&gt;SCRIPT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vm"&gt;__file__&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; started&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# A and B spawn a subprocess&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="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# Sleep and wait for a Ctrl-C&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; done&amp;#39;&lt;/span&gt; &lt;span class="o"&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;except&lt;/span&gt; &lt;span class="ne"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got KeyboardInterrupt&amp;#39;&lt;/span&gt; &lt;span class="o"&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;finally&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;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&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;subproc&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="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create and return a new subprocess named *name*.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;PYTHON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SCRIPT&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;return&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;main&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;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running this script and pressing &lt;em&gt;Ctrl-C&lt;/em&gt; after about one or two seconds gives
us the following&amp;nbsp;output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;subprocs_1.py
A&lt;span class="w"&gt; &lt;/span&gt;started
B&lt;span class="w"&gt; &lt;/span&gt;started
C&lt;span class="w"&gt; &lt;/span&gt;started
^CA&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;KeyboardInterrupt
B&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;KeyboardInterrupt
C&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;KeyboardInterrupt
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So far so good. It will get more complicated&amp;nbsp;soon.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="stopping-a-sub-process-and-all-of-its-children"&gt;
&lt;h2&gt;Stopping a sub-process and all of its&amp;nbsp;children&lt;/h2&gt;
&lt;p&gt;To request the termination of a subprocess, we’ll send a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; to it
(on Linux and &lt;span class="caps"&gt;OS&lt;/span&gt; X, this signal is sent if you call a Popen object’s
&lt;a href="http://docs.python.org/3.3/library/subprocess#subprocess.Popen.terminate"&gt;terminate()&lt;/a&gt; method). For the process being terminated, this means the same
as a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;—it is allowed to do some clean-up or even to ignore the&amp;nbsp;signal.&lt;/p&gt;
&lt;p&gt;To catch the signal, you have to register a custom handler, though. To make
things easier, you should also register the same handler for the
&lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; signal, so that you don’t end up having the same code in the
handler function and in an &lt;code&gt;except KeyboardInterrupt&lt;/code&gt; block:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;traceback&lt;/span&gt;


&lt;span class="n"&gt;PYTHON&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;
&lt;span class="n"&gt;SCRIPT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vm"&gt;__file__&lt;/span&gt;
&lt;span class="n"&gt;SIGNALS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SIGINT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SIGTERM&amp;#39;&lt;/span&gt;&lt;span class="p"&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;main&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="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;If *terminate* is ``True`` (should only be the case if *name* is ``A``),&lt;/span&gt;
&lt;span class="sd"&gt;    A will try to terminate B.&lt;/span&gt;

&lt;span class="sd"&gt;    B and C will always just sleep and wait for things to happen ...&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; started&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# A and B spawn a subprocess&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="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# Curry our cleanup func and register it as handler for SIGINT and SIGTERM&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleanup&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="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&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;terminate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# A tries to terminate B&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; ended&amp;#39;&lt;/span&gt; &lt;span class="o"&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;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; done&amp;#39;&lt;/span&gt; &lt;span class="o"&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;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&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;subproc&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="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create and return a new subprocess named *name*.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;PYTHON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SCRIPT&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;return&lt;/span&gt; &lt;span class="n"&gt;proc&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send a SIGTERM to *proc* and wait for it to terminate.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Sends SIGTERM&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&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;cleanup&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="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Stop the sub-process *child* if *signum* is SIGTERM. Then terminate.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;SIGNALS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;signum&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;child&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;signum&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_exc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;term&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&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;# B or C&lt;/span&gt;

    &lt;span class="n"&gt;main&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="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our processes now handle &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; and &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; signals via the
&lt;code&gt;cleanup()&lt;/code&gt; handler. The curried version of this handler will have the name
of the process and its child process set by default, leaving its signature as
&lt;code&gt;handler(signum, frame)&lt;/code&gt; which is exactly what &lt;code&gt;signal.signal()&lt;/code&gt; expects.
Note, that we don’t need to terminate our sub-processes when we get
a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;, since the sub-processes will also receive&amp;nbsp;one.&lt;/p&gt;
&lt;p&gt;We can now also pass a &lt;em&gt;terminate&lt;/em&gt; argument to our script. &lt;em&gt;A&lt;/em&gt; will then try to
terminate &lt;em&gt;B&lt;/em&gt; after a second. &lt;em&gt;B&lt;/em&gt; will then stop &lt;em&gt;C&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you run this script without arguments, you will get (nearly) the same output
as in the last example. If you pass &lt;code&gt;term&lt;/code&gt;, you’ll get the following output
from a Linux or &lt;span class="caps"&gt;OS&lt;/span&gt; X&amp;nbsp;shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;subprocs_2.py&lt;span class="w"&gt; &lt;/span&gt;term
A&lt;span class="w"&gt; &lt;/span&gt;started
B&lt;span class="w"&gt; &lt;/span&gt;started
C&lt;span class="w"&gt; &lt;/span&gt;started
B&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;SIGTERM
C&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;SIGTERM
A&lt;span class="w"&gt; &lt;/span&gt;ended
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run the same thing on Windows, you’ll&amp;nbsp;get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;python subprocs_2.py term
A started
B started
C started
A ended

&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;C done
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our code now magically stopped working on Windows :-). &lt;em&gt;B&lt;/em&gt; seems to receive
something from &lt;em&gt;A&lt;/em&gt;, but immediately terminates (no output is printed). It also
does not forward the signal to &lt;em&gt;C&lt;/em&gt;, so &lt;em&gt;C&lt;/em&gt; just waits ten seconds and prints
its message before exiting on its&amp;nbsp;own.&lt;/p&gt;
&lt;p&gt;Clearly not the desired behavior, so this is where the fun begins&amp;nbsp;…&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fixing-it-on-windows"&gt;
&lt;h2&gt;Fixing it on&amp;nbsp;Windows&lt;/h2&gt;
&lt;p&gt;If you search for this problem on the Interwebs, you’ll find a lot of different
answers—some more helpful, some less. This &lt;a href="http://www.reddit.com/r/Python/comments/1dsblt/windows_command_line_automation_ctrlc_question/"&gt;Reddit post&lt;/a&gt; helped me the most.
According to the &lt;span class="upper"&gt;&lt;span class="caps"&gt;MSDN&lt;/span&gt;&lt;/span&gt;, you can use &lt;cite&gt;GenerateConsoleCtrlEvent&lt;/cite&gt; to send
two types of signals to a process: &lt;span class="upper"&gt;CTRL_C_EVENT&lt;/span&gt; and
&lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt;. The former translates to a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt;, the
latter to a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGBREAK&lt;/span&gt;&lt;/span&gt;. Instead of &lt;code&gt;GenerateConsoleCtrlEvent&lt;/code&gt;, which
is only available via the win32 &lt;span class="upper"&gt;&lt;span class="caps"&gt;API&lt;/span&gt;&lt;/span&gt;, you can fortunately also use
&lt;code&gt;os.kill&lt;/code&gt; or &lt;code&gt;Popen.send_signal()&lt;/code&gt; to send the&amp;nbsp;signal.&lt;/p&gt;
&lt;p&gt;A &lt;span class="upper"&gt;CTRL_C_EVENT&lt;/span&gt; can &lt;em&gt;not&lt;/em&gt; be directed to a single process and is always
received by all processes that share the current console.
&lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt; on the other hand can be send to specific processes.
However, in order to use it, we have to pass
&lt;code&gt;creationgflags-subprocess.CREATE_NEW_PROCESS_GROUP&lt;/code&gt; to the &lt;a href="http://docs.python.org/3.3/library/subprocess#popen-constructor"&gt;Popen&lt;/a&gt;
constructor. This parameter is only available on Windows and has nothing to do
with Unix process groups. When you set this flag, you can &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms684863%28v-vs.85%29.aspx"&gt;no longer&lt;/a&gt; send
&lt;span class="upper"&gt;CTRL_C_EVENT&lt;/span&gt; and if you press &lt;em&gt;Ctrl-C&lt;/em&gt; in your console, only the root
process will receive a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; signal. Also note, that sending any
other signal than &lt;span class="upper"&gt;CTRL_C_EVENT&lt;/span&gt; and &lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt; will
unconditionally &lt;a href="http://docs.python.org/3/library/os#os.kill"&gt;kill the process&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’ll end up with the following&amp;nbsp;preconditions:&lt;/p&gt;
&lt;p&gt;If on Linux or &lt;span class="caps"&gt;OS&lt;/span&gt;&amp;nbsp;X:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your terminal sends a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; to all processes if you press &lt;em&gt;Ctrl-C&lt;/em&gt;.
There’s no need to forward it to&amp;nbsp;sub-processes.&lt;/li&gt;
&lt;li&gt;If a process receives a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt;, it should forward it to its&amp;nbsp;sub-processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If on&amp;nbsp;Windows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You should start sub-processes with
&lt;code&gt;creationflags-CREATE_NEW_PROCESS_GROUP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;This enables you to send a &lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt; to a specific process.
The process receives a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGBREAK&lt;/span&gt;&lt;/span&gt; and should forward it to its&amp;nbsp;sub-processes.&lt;/li&gt;
&lt;li&gt;If you press &lt;em&gt;Ctrl-C&lt;/em&gt; in your console, only the root process receives
a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; and should send a &lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt; to its&amp;nbsp;sub-processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id="example"&gt;Incorporating what we know now, our example looks like&amp;nbsp;this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&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;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;traceback&lt;/span&gt;


&lt;span class="n"&gt;PYTHON&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;
&lt;span class="n"&gt;SCRIPT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="vm"&gt;__file__&lt;/span&gt;
&lt;span class="n"&gt;ON_WINDOWS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;win32&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;SIGNALS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SIGINT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SIGTERM&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&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;ON_WINDOWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;SIGNALS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGBREAK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SIGBREAK&amp;#39;&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&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="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;If *terminate* is ``True`` (should only be the case if *name* is ``A``),&lt;/span&gt;
&lt;span class="sd"&gt;    A will try to terminate B.&lt;/span&gt;

&lt;span class="sd"&gt;    B and C will always just sleep and wait for things to happen ...&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; started&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# A and B spawn a subprocess&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="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subproc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# Curry our cleanup func and register it as handler for SIGINT and SIGTERM&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;partial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleanup&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="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&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;ON_WINDOWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGBREAK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&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;terminate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# A tries to terminate B&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; ended&amp;#39;&lt;/span&gt; &lt;span class="o"&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;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# SIGBREAK cannot interrupt sleep(), so we sleep 10 * 1s instead&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; done&amp;#39;&lt;/span&gt; &lt;span class="o"&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;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&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;subproc&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="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create and return a new subprocess named *name*.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;-&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;ON_WINDOWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;creationflags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CREATE_NEW_PROCESS_GROUP&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;PYTHON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SCRIPT&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;proc&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send a SIGTERM/SIGBREAK to *proc* and wait for it to terminate.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ON_WINDOWS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CTRL_BREAK_EVENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&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;cleanup&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="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Stop the sub-process *child* if *signum* is SIGTERM. Then terminate.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; got a &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&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="n"&gt;SIGNALS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;signum&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;child&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ON_WINDOWS&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;signum&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Forward SIGTERM on Linux or any signal on Windows&lt;/span&gt;
            &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;traceback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_exc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;term&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;terminate&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;A&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&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;# B or C&lt;/span&gt;

    &lt;span class="n"&gt;main&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="n"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can register a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; handler in any case. If on Windows we also
register a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGBREAK&lt;/span&gt;&lt;/span&gt; handler. For all remaining cases we register
a handler for &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Since &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGBREAK&lt;/span&gt;&lt;/span&gt; cannot interrupt a &lt;code&gt;time.sleep()&lt;/code&gt; call, I changed it
from &lt;code&gt;time.sleep(10)&lt;/code&gt; to &lt;code&gt;for i in range(10): time.sleep(1)&lt;/code&gt; so that the
sub-processes terminate faster. I don’t know if this is intended behavior or
a bug in&amp;nbsp;Python.&lt;/p&gt;
&lt;p&gt;When terminating a process, you have to send a &lt;span class="upper"&gt;CTRL_BREAK_EVENT&lt;/span&gt; on
Windows. Else, you can just use &lt;code&gt;Popen.terminate()&lt;/code&gt; which will send
a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGTERM&lt;/span&gt;&lt;/span&gt; to the&amp;nbsp;process.&lt;/p&gt;
&lt;p&gt;In our clean-up function, we only forward the signal if its not
a &lt;span class="upper"&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/span&gt; or if we are on Windows. And if there is child process, of&amp;nbsp;course.&lt;/p&gt;
&lt;p&gt;The output on Linux stays the same, but on Windows, we now&amp;nbsp;get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;python subprocs_3.py term
A started
B started
C started
B got a SIGBREAK
C got a SIGBREAK
A ended
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It finally works on Linux, &lt;span class="caps"&gt;OS&lt;/span&gt; X &lt;em&gt;and&lt;/em&gt; Windows!&amp;nbsp;\o/&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you don’t have to (or don’t want to) support Windows, handling sub-processes
and sending signals to them is not that hard. But I hope I could demonstrate
that it’s—despite of all problems—possible to get the job done on Windows, too,
without too much&amp;nbsp;overhead.&lt;/p&gt;
&lt;p&gt;An alternative to signals, if you are using some other kind of &lt;span class="upper"&gt;&lt;span class="caps"&gt;IPC&lt;/span&gt;&lt;/span&gt;
anyways, might be using it to send a termination requests to your processes
instead of sending a signal. There would be no need for the
&lt;span class="upper"&gt;CREATE_NEW_PROCESS_GROUP&lt;/span&gt; flag then and &lt;em&gt;Ctrl-C&lt;/em&gt; would work nicely on
all platforms. You may take care though, that clients are not able to send
a termination message to your server, but that depends on your use&amp;nbsp;case.&lt;/p&gt;
&lt;p&gt;In the end, I wish Windows was Posix-compliant. Would make life so much easier&amp;nbsp;…&lt;/p&gt;
&lt;/section&gt;
</content><category term="2013"></category><category term="python"></category><category term="windows"></category><category term="linux"></category><category term="os x"></category><category term="signals"></category></entry><entry><title>Bitbucket stopped rendering README.txt for Python projects [resolved]</title><link href="https://stefan.sofa-rockers.org/2013/01/11/bitbucket-stopped-rendering-readmetxt-python-proje/" rel="alternate"></link><published>2013-01-11T09:34:29+01:00</published><updated>2013-01-11T09:34:29+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2013-01-11:/2013/01/11/bitbucket-stopped-rendering-readmetxt-python-proje/</id><summary type="html">&lt;p&gt;Until recently, Bitbucket nicely rendered reStructuredText (reST) formatted
&lt;span class="caps"&gt;README&lt;/span&gt;.txt files for Python projects. This made totally sense, because &lt;a href="http://pypi.python.org/pypi"&gt;PyPI&lt;/a&gt; requires the project’s description in reST and
most people …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Until recently, Bitbucket nicely rendered reStructuredText (reST) formatted
&lt;span class="caps"&gt;README&lt;/span&gt;.txt files for Python projects. This made totally sense, because &lt;a href="http://pypi.python.org/pypi"&gt;PyPI&lt;/a&gt; requires the project’s description in reST and
most people put it into &lt;span class="caps"&gt;README&lt;/span&gt;.txt&amp;nbsp;files.&lt;/p&gt;
&lt;p&gt;However, for some reason the Bitbucket guys removed that feature and it seems
that they don’t intend to bring it back. Instead, you are urged to rename your
files from &lt;em&gt;*.txt&lt;/em&gt; to &lt;em&gt;*.rst&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Maybe some &lt;a href="https://bitbucket.org/site/master/issue/5617/readme-not-rendered-properly-on-the"&gt;feedback&lt;/a&gt;
would help to bring that feature back&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Bitbucket fixed that issue.&amp;nbsp;:-)&lt;/p&gt;
</content><category term="2013"></category><category term="python"></category><category term="bitbucket"></category><category term="restructuredtext"></category></entry><entry><title>SimPy 3 Preview</title><link href="https://stefan.sofa-rockers.org/2012/12/12/simpy-3-preview/" rel="alternate"></link><published>2012-12-12T15:15:22+01:00</published><updated>2012-12-12T15:15:22+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-12-12:/2012/12/12/simpy-3-preview/</id><summary type="html">&lt;p&gt;&lt;a href="https://simpy.readthedocs.org/en/latest/"&gt;SimPy&lt;/a&gt; is a process-based and
event-driven simulation framework written in pure Python. It can also be used
for multi-agent systems and other eventloop-based&amp;nbsp;applications.&lt;/p&gt;
&lt;p&gt;After several months of work and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://simpy.readthedocs.org/en/latest/"&gt;SimPy&lt;/a&gt; is a process-based and
event-driven simulation framework written in pure Python. It can also be used
for multi-agent systems and other eventloop-based&amp;nbsp;applications.&lt;/p&gt;
&lt;p&gt;After several months of work and various iterations of SimPy’s new &lt;span class="caps"&gt;API&lt;/span&gt;, we
can finally present a preview with the most important features&amp;nbsp;working.&lt;/p&gt;
&lt;p&gt;Here is a simple&amp;nbsp;example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;simpy&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;Process(clock)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;simpy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;simulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;0&lt;/span&gt;
&lt;span class="go"&gt;1&lt;/span&gt;
&lt;span class="go"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find the full announcement at &lt;a href="https://plus.google.com/u/0/101634625602509193865/posts/196xrYcKsuL"&gt;Google+&lt;/a&gt; and on
our &lt;a href="http://sourceforge.net/mailarchive/message.php?msg_id=30218376"&gt;mailing list&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The code is at &lt;a href="https://bitbucket.org/simpy/simpy"&gt;Bitbucket&lt;/a&gt;, the
documentation at &lt;a href="https://simpy.readthedocs.org/en/latest/"&gt;Read the Docs&lt;/a&gt;.&lt;/p&gt;
</content><category term="2012"></category><category term="python"></category><category term="scientific"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>Check Python site-packages for Updates</title><link href="https://stefan.sofa-rockers.org/2012/04/14/check-python-site-packages-updates/" rel="alternate"></link><published>2012-04-14T11:50:50+02:00</published><updated>2012-04-14T11:50:50+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-04-14:/2012/04/14/check-python-site-packages-updates/</id><summary type="html">&lt;p&gt;A while ago, I found a nice little script called &lt;a href="https://gist.github.com/910447"&gt;check_for_updates.py&lt;/a&gt; which uses &lt;span class="caps"&gt;PIP&lt;/span&gt; to check your installed
Python packages for updates. However, it didn’t work under Python …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A while ago, I found a nice little script called &lt;a href="https://gist.github.com/910447"&gt;check_for_updates.py&lt;/a&gt; which uses &lt;span class="caps"&gt;PIP&lt;/span&gt; to check your installed
Python packages for updates. However, it didn’t work under Python 3, so
I ported it&amp;nbsp;myself:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;Use pip to get a list of local packages to check against one or more package&lt;/span&gt;
&lt;span class="sd"&gt;indexes for updated versions.&lt;/span&gt;

&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;cStringIO&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;xmlrpclib&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;xmlrpc.client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;xmlrpclib&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;distutils.version&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StrictVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LooseVersion&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;pip&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_local_packages&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Call pip&amp;#39;s freeze -l&lt;/span&gt;

&lt;span class="sd"&gt;    returns a list of package_name, version tuples&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mystdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StringIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;freeze&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;-l&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__stdout__&lt;/span&gt;

    &lt;span class="n"&gt;pkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mystdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;==&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pkgs&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;find_current_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_urls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Using the XMLRPC method available for PyPI, get the most recent version&lt;/span&gt;
&lt;span class="sd"&gt;    of &amp;lt;package&amp;gt; from each of the index_urls and figure out which one (if any)&lt;/span&gt;
&lt;span class="sd"&gt;    is higher&lt;/span&gt;

&lt;span class="sd"&gt;    Returns a tuple of the index with the higher version and the version it has&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index_urls&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;index_urls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://pypi.python.org/pypi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;cur_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;cur_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index_url&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;index_urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pypi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmlrpclib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServerProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xmlrpclib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;pypi_hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pypi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;package_releases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pypi_hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;compare_versions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pypi_hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cur_version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;cur_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pypi_hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;cur_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index_url&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cur_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cur_version&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compare_versions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Compare 2 versions, starting with StrictVersion, and falling back on&lt;/span&gt;
&lt;span class="sd"&gt;    LooseVersion. Returns ``True`` if *version1* is greater than *version2*.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&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;StrictVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;StrictVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# in case of abnormal version number, fall back to LooseVersion&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&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;LooseVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LooseVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version2&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;output_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Output the line showing the formatted information.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%(bd)s%(pkg_name)s%(nm)s&lt;/span&gt;&lt;span class="s1"&gt; (&lt;/span&gt;&lt;span class="si"&gt;%(new)s&lt;/span&gt;&lt;span class="s1"&gt;) via &lt;/span&gt;&lt;span class="si"&gt;%(index)s&lt;/span&gt;&lt;span class="s1"&gt;. Currently &lt;/span&gt;&lt;span class="si"&gt;%(old)s&lt;/span&gt;&lt;span class="s1"&gt;.&amp;#39;&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;bd&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;nm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NORMAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;pkg_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pkg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;old&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;old_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;index_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;NEWER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;compare_versions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;y&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="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;curses&lt;/span&gt;
    &lt;span class="n"&gt;curses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setupterm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;CLEAR_SCREEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tigetstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;clear&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;BOLD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tigetstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bold&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;NORMAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tigetstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sgr0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://pypi.python.org/pypi&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CLEAR_SCREEN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;BOLD&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Packages with newer versions:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;NORMAL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;get_local_packages&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# pip outputs a single 0 at the end of the list. Ignore it.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_current_version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;index_urls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;indexes&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;current_version&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;NEWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_version&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="n"&gt;output_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;current_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Just execute it from anywhere using a Python interpreter whose packages you’d
like to check (e.g., &lt;code&gt;python3 check_for_updates.py&lt;/code&gt; or from within
a virtualenv). You can then uses &lt;code&gt;pip install -U &amp;lt;packagename&amp;gt;&lt;/code&gt; (or
&lt;code&gt;pip-3.2&lt;/code&gt; for Python 3) to update the&amp;nbsp;packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; The &lt;a href="http://pypi.python.org/pypi/yolk"&gt;yolk&lt;/a&gt; package does the
same via &lt;code&gt;yolk -U&lt;/code&gt;. It doesn’t support Python 3, though. I wonder when &lt;span class="caps"&gt;PIP&lt;/span&gt;
will get that&amp;nbsp;functionality.&lt;/p&gt;
</content><category term="2012"></category><category term="python"></category><category term="pypi"></category><category term="pip"></category></entry><entry><title>A Simple Web Bot with Requests and BeautifulSoup</title><link href="https://stefan.sofa-rockers.org/2012/02/21/simple-web-bot-requests-and-beautifulsoup/" rel="alternate"></link><published>2012-02-21T12:59:51+01:00</published><updated>2012-02-21T12:59:51+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-02-21:/2012/02/21/simple-web-bot-requests-and-beautifulsoup/</id><summary type="html">&lt;p&gt;Today I helped a colleague debugging a web bot written in Java. Since I did’t
really work with Java since a few years, I thought it would be easier …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Today I helped a colleague debugging a web bot written in Java. Since I did’t
really work with Java since a few years, I thought it would be easier for me to
reproduce (and solve) the problem with &lt;a href="http://docs.python-requests.org/en/latest/"&gt;Requests&lt;/a&gt; and &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt;. (I’ve actually been looking
for an opportunity to try Requests out for a while, since I’ve heard so much
good about&amp;nbsp;it.)&lt;/p&gt;
&lt;p&gt;And what can I say—I was blown away by how easy it was to implement a simple
web bot that filled out a form and grabbed a huge table of data for&amp;nbsp;me.&lt;/p&gt;
&lt;p&gt;I cannot show you exactly that web bot, but here’s how you could search my
website for “Python” and get the headings of the resulting&amp;nbsp;posts:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;BeautifulSoup&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://stefan.sofa-rockers.org/search/?q=&lt;/span&gt;&lt;span class="si"&gt;%(q)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;q&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Python&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;h2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;post_title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="2012"></category><category term="python"></category><category term="requests"></category><category term="beautifulsoup"></category></entry><entry><title>Designing and Testing PyZMQ Applications – Part 3</title><link href="https://stefan.sofa-rockers.org/2012/02/15/designing-and-testing-pyzmq-applications-part-3/" rel="alternate"></link><published>2012-02-15T18:20:22+01:00</published><updated>2012-02-15T18:20:22+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-02-15:/2012/02/15/designing-and-testing-pyzmq-applications-part-3/</id><summary type="html">&lt;p&gt;The third and last part of this &lt;a href="/2012/02/01/designing-and-testing-pyzmq-applications-part-1/"&gt;series&lt;/a&gt; is again just about
testing. While the &lt;a href="/2012/02/07/designing-and-testing-pyzmq-applications-part-2/"&gt;previous article&lt;/a&gt; focused on
unit testing, this one will be about testing complete PyZMQ processes …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The third and last part of this &lt;a href="/2012/02/01/designing-and-testing-pyzmq-applications-part-1/"&gt;series&lt;/a&gt; is again just about
testing. While the &lt;a href="/2012/02/07/designing-and-testing-pyzmq-applications-part-2/"&gt;previous article&lt;/a&gt; focused on
unit testing, this one will be about testing complete PyZMQ processes. This
even involves some &lt;a href="http://bit.ly/g3wTBC"&gt;magic&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Once you’ve made sure that your message dispatching and application logic works
fine, you can actually start sending real messages to your process and checking
real replies. This can be done for single processes—I call this &lt;em&gt;process
testing&lt;/em&gt;—and for your complete application (&lt;em&gt;system testing&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;When you test a single process, you create sockets that mimic all processes the
tested process communicates with. When you do a system test, you only mimic a
client or just invoke your program from the command line and check its output
(e.g., what it prints to &lt;em&gt;stdtout&lt;/em&gt; and &lt;em&gt;stderr&lt;/em&gt; or results written to a&amp;nbsp;database).&lt;/p&gt;
&lt;p&gt;I’ll start with process testing, which is a bit more generalizable than system&amp;nbsp;testing.&lt;/p&gt;
&lt;section id="process-testing"&gt;
&lt;h2&gt;Process&amp;nbsp;Testing&lt;/h2&gt;
&lt;p&gt;The biggest problem I ran into when I started testing processes was that I
often made blocking calls to &lt;em&gt;recv&lt;/em&gt; methods and these halted my tests and gave
me no output about what actually went wrong. Though you can make them non-
blocking by passing &lt;code&gt;zmq.NOBLOCK&lt;/code&gt; as an extra argument, this doesn’t solve
your problems. You will now need a very precise timing and many
&lt;code&gt;time.sleep(x)&lt;/code&gt; calls, because &lt;em&gt;recv&lt;/em&gt; will instantly raise an error if there
is nothing to be&amp;nbsp;received.&lt;/p&gt;
&lt;p&gt;My solution for this was to wrap PyZMQ sockets and add a timeout to its &lt;em&gt;send&lt;/em&gt;
and &lt;em&gt;recv&lt;/em&gt; methods. The following wrapper will try to receive something for one
second and raise an exception if that failed. There’s also &lt;a href="https://bitbucket.org/sscherfke/pyzmq-article/src/tip/example_app/test/support.py#cl-27"&gt;a simple wrapper&lt;/a&gt;
for methods like &lt;em&gt;connect&lt;/em&gt; or &lt;em&gt;bind&lt;/em&gt;, but it’s really not that interesting, so
I’ll omit it&amp;nbsp;here.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/support.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_wrapped_fwd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Returns a wrapper, that tries to call *func* multiple time in non-blocking&lt;/span&gt;
&lt;span class="sd"&gt;    mode before rasing an :class:`zmq.ZMQError`.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forwarder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 100 tries * 0.01 second == 1 second&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NOBLOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&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;rep&lt;/span&gt;

            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZMQError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# We should not get here, so raise an error.&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Could not &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; message.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZMQError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&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;forwarder&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This wrapper is now used to create a &lt;em&gt;TestSocket&lt;/em&gt; class with the desired&amp;nbsp;behavior:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/support.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Wraps ZMQ :class:`~zmq.core.socket.Socket`. All *recv* and *send* methods&lt;/span&gt;
&lt;span class="sd"&gt;    will be called multiple times in non-blocking mode before a&lt;/span&gt;
&lt;span class="sd"&gt;    :class:`zmq.ZMQError` is raised.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;

        &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;

        &lt;span class="n"&gt;forwards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="c1"&gt;# These methods can simply be forwarded&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_to_random_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;wrapped_fwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  &lt;span class="c1"&gt;# These methods are wrapped with a for loop&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_multipart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_unicode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_multipart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_unicode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;setattr&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;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_forwarder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;wrapped_fwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nb"&gt;setattr&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;func&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_wrapped_fwd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In order to reuse the same ports for all test methods, you need to cleanly
close all sockets after each test. To handle method level setup/teardown in
pytest, you need to implement a &lt;em&gt;setup_method&lt;/em&gt; and a &lt;em&gt;teardown_method&lt;/em&gt;. In the
setup method, you create one or more &lt;em&gt;TestSocket&lt;/em&gt; instances that mimic other
processes and you also start the process to be&amp;nbsp;tested:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/process/test_pongproc.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;test.support&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProcessTest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;make_sock&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pongproc&lt;/span&gt;


&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5678&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestProngProc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProcessTest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Communication test for the Platform Manager process.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_method&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;method&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Creates and starts a PongProc process and sets up sockets to&lt;/span&gt;
&lt;span class="sd"&gt;        communicate with it.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# make_sock creates and connects a TestSocket that we will use to&lt;/span&gt;
        &lt;span class="c1"&gt;# mimic the Ping process&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_sock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PongProc&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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;teardown_method&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;method&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Sends a kill message to the pp and waits for the process to terminate.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Send a stop message to the prong process and wait until it joins&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_multipart&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;plzdiekthxbye&amp;quot;, null]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You may have noticed that our test class inherits &lt;em&gt;ProcessTests&lt;/em&gt;. This class
and some helpers in a &lt;a href="http://pytest.org/latest/plugins.html"&gt;conftest.py&lt;/a&gt;
allow us to use some magic that improves the readability of the actual&amp;nbsp;test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/process/test_pongproc.py&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_ping&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Tests a ping-pong sequence.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;send&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ping&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;recv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can just yield &lt;em&gt;send&lt;/em&gt; or &lt;em&gt;recv&lt;/em&gt; events from your test case! When you yield
a &lt;em&gt;send&lt;/em&gt;, the test machinery tries to send a message via the specified socket.
When you yield a receive, &lt;em&gt;ProcessTest&lt;/em&gt; tries to receive something from the
socket and sends its result back to your test function, so that you can easily
compare the reply with the expected&amp;nbsp;result.&lt;/p&gt;
&lt;p&gt;The example above is roughly equivalent to the following&amp;nbsp;code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_multipart&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ping&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])])&lt;/span&gt;

&lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;req_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_multipart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;reply&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;reply&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So how does this work? By default, if pytests finds a test function that is a
generator, it assumes that it generates further test functions. Hence, our
first step is to override this behavior. We can do this in a &lt;code&gt;conftest.py&lt;/code&gt;
file in the &lt;code&gt;test/process/&lt;/code&gt; directory by implementing a
&lt;em&gt;pytest_pycollect_makeitem&lt;/em&gt; function. In this case, we collect generator
functions like normal&amp;nbsp;functions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/process/conftest.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;inspect&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;isfunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isgeneratorfunction&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_pycollect_makeitem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collector&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="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Collects all instance methods that are generators and returns them as&lt;/span&gt;
&lt;span class="sd"&gt;    normal function items.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;funcnamefilter&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="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__call__&amp;#39;&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;isfunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;isgeneratorfunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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;collector&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_genfunctions&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="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, we need to tell pytest how to run a test on the collected generator
functions. This can be done by implementing &lt;em&gt;pytest_runtest_call&lt;/em&gt;. If the
object we are going to test (&lt;code&gt;item.obj&lt;/code&gt;) is a generator function, we call the
&lt;em&gt;run&lt;/em&gt; method  of the object’s instance (&lt;code&gt;item.obj.__self__.run&lt;/code&gt;) and pass the
generator function to it. If the test item contains a normal function, we run
the default&amp;nbsp;test.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/process/conftest.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_runtest_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Passes the test generator (``item.obj``) to the ``run()`` method of the&lt;/span&gt;
&lt;span class="sd"&gt;    generator&amp;#39;s instance. This method should be inherited from&lt;/span&gt;
&lt;span class="sd"&gt;    :class:`test.support.ProcessTest`.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isgeneratorfunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__self__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&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;# Normal test execution for normal instance methods&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runtest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But wait—we didn’t implement a &lt;em&gt;run&lt;/em&gt; method in our test case! So it must be
inherited from &lt;em&gt;ProcessTest&lt;/em&gt;. Let’s take a look at&amp;nbsp;it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/support.py&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcessTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Base class for process tests. It offers basic actions for sending and&lt;/span&gt;
&lt;span class="sd"&gt;    receiving messages and implements the *run* methods that handles the&lt;/span&gt;
&lt;span class="sd"&gt;    actual test generators.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&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;testfunc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Iterates over the *testfunc* generator and executes all actions it&lt;/span&gt;
&lt;span class="sd"&gt;        yields. Results will be sent back into the generator.&lt;/span&gt;

&lt;span class="sd"&gt;        :param testfunc: A generator function that yields tuples containing&lt;/span&gt;
&lt;span class="sd"&gt;                an action keyword, which should be a function of this or&lt;/span&gt;
&lt;span class="sd"&gt;                the inheriting class (like ``send`` or ``recv``) and additional&lt;/span&gt;
&lt;span class="sd"&gt;                parameters that will be passed to that function, e.g.:&lt;/span&gt;
&lt;span class="sd"&gt;                ``(&amp;#39;send&amp;#39;, socket_obj, [&amp;#39;header&amp;#39;], &amp;#39;body&amp;#39;)``&lt;/span&gt;
&lt;span class="sd"&gt;        :type testfunc:  generatorfunction&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;item_gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;testfunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_gen&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;throw_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skip_levels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;            Throws the last error to *item_gen* and skips *skip_levels* in&lt;/span&gt;
&lt;span class="sd"&gt;            the traceback to point to the line that yielded the last event.&lt;/span&gt;

&lt;span class="sd"&gt;            &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
            &lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evalue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skip_levels&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;tb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tb_next&lt;/span&gt;
            &lt;span class="n"&gt;item_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evalue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="c1"&gt;# Call the event handler and pass the args,&lt;/span&gt;
                    &lt;span class="c1"&gt;# e.g., self.send(socket_obj, header, body)&lt;/span&gt;
                    &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&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;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&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;# Send the results back to the test and get the next item&lt;/span&gt;
                    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item_gen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZMQError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;throw_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# PyZMQ could not send/recv&lt;/span&gt;
                &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;throw_err&lt;/span&gt;&lt;span class="p"&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;# Error in the test&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;em&gt;run&lt;/em&gt; method simply iterates over all events our &lt;em&gt;testfunc&lt;/em&gt; generates and
calls a method with the name of the event (e.g., &lt;em&gt;send&lt;/em&gt; or &lt;em&gt;recv&lt;/em&gt;). Their
return value is sent back into the generator. If an error occurs, the
exception’s traceback is modified to point to the line of code that yielded the
according event and not to the &lt;em&gt;run&lt;/em&gt; method&amp;nbsp;itself.&lt;/p&gt;
&lt;p&gt;The methods &lt;em&gt;send&lt;/em&gt; and &lt;em&gt;recv&lt;/em&gt; roughly do the same as the snippet I showed you&amp;nbsp;above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/support.py&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&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;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        JSON-encodes *body*, concatenates it with *header*, appends&lt;/span&gt;
&lt;span class="sd"&gt;        *extra_data* and sends it as multipart message over *socket*.&lt;/span&gt;

&lt;span class="sd"&gt;        *header* and *extra_data* should be lists containg byte objects or&lt;/span&gt;
&lt;span class="sd"&gt;        objects implementing the buffer interface (like NumPy arrays).&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_multipart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;extra_data&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;recv&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;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_load_index&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Receives and returns a multipart message from *socket* and tries to&lt;/span&gt;
&lt;span class="sd"&gt;        JSON-decode the item at position *json_load_index* (defaults to ``-1``;&lt;/span&gt;
&lt;span class="sd"&gt;        the last element in the list). The original byte string will be&lt;/span&gt;
&lt;span class="sd"&gt;        replaced by the loaded object. Set *json_load_index* to ``None`` to get&lt;/span&gt;
&lt;span class="sd"&gt;        the original, unchanged message.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_multipart&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;json_load_index&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json_load_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json_load_index&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;msg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can even add your own event handler to your test class. I used this, for
example, to add a &lt;em&gt;log&lt;/em&gt; event that checks if a PyZMQ log handler sent the
expected log&amp;nbsp;messages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log&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;substr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Receives a message and asserts, that it is a log message and that&lt;/span&gt;
&lt;span class="sd"&gt;    *substr* is in that message.&lt;/span&gt;

&lt;span class="sd"&gt;    Usage:&lt;/span&gt;
&lt;span class="sd"&gt;        yield (&amp;#39;log&amp;#39;, &amp;#39;Ai iz in ur log mesage&amp;#39;)&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;log_message&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;substr&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;section id="what-if-your-process-starts-further-subprocesses"&gt;
&lt;h3&gt;What if your process starts further&amp;nbsp;subprocesses?&lt;/h3&gt;
&lt;p&gt;In some cases, the process you are about to test starts additional subprocesses
that you don’t want to test. Even worse, these processes might communicate via
sockets bound to random ports. And &lt;span class="caps"&gt;EVEN&lt;/span&gt; &lt;span class="caps"&gt;WORSE&lt;/span&gt;, the process you are testing
might depend on excepting a &lt;em&gt;KeyboardInterrupt&lt;/em&gt; to send stop messages to child
processes or to clean something&amp;nbsp;up!&lt;/p&gt;
&lt;p&gt;The last problem is quite easy to solve: You just a send a &lt;em&gt;&lt;span class="caps"&gt;SIGINT&lt;/span&gt;&lt;/em&gt; to your
process from the&amp;nbsp;test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;signal&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;teardown_method&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;method&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Now you can close the test sockets&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you don’t want to start a certain subprocess, you can just mock it.
Imagine, you have two processes &lt;code&gt;a.A&lt;/code&gt; and &lt;code&gt;b.B&lt;/code&gt;, where &lt;em&gt;A&lt;/em&gt; starts &lt;em&gt;B&lt;/em&gt;,
then you just mock &lt;em&gt;B&lt;/em&gt; before starting &lt;em&gt;A&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b.B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Imagine now, that &lt;em&gt;A&lt;/em&gt; binds a socket to a random port and uses that socket to
communicate with &lt;em&gt;B&lt;/em&gt;. If you want to mock &lt;em&gt;B&lt;/em&gt; in your tests, you need that port
number in order to connect to it and send messages to &lt;em&gt;A&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But how can you get that number? When &lt;em&gt;A&lt;/em&gt; creates &lt;em&gt;B&lt;/em&gt;, it already runs in its
own process, so a simple attribute access won’t work. Setting a random seed
would only work if you did that directly in &lt;em&gt;A&lt;/em&gt; when it’s already running. But
doing that just for the tests is not such a good idea. It also may not work
reliably on all systems and Python&amp;nbsp;versions.&lt;/p&gt;
&lt;p&gt;However, &lt;em&gt;A&lt;/em&gt; must pass the socket number to &lt;em&gt;B&lt;/em&gt;, so that &lt;em&gt;B&lt;/em&gt; can connect to
&lt;em&gt;A&lt;/em&gt;. Thus, we can create a mock for &lt;em&gt;B&lt;/em&gt; that will send us its port number via a
&lt;cite&gt;queue &amp;lt;http://docs.python.org/py3k/library/multiprocessing#exchanging-objects-
between-processes&amp;gt;&lt;/cite&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProcMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    This mock returns itself when called, so it acts like both, the&lt;/span&gt;
&lt;span class="sd"&gt;    process’ class and instance object.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiprocessing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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;port&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Will be called when A instantiates B and passes its port number.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&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;return&lt;/span&gt;  &lt;span class="c1"&gt;# Just make sure the methods exists and returns nothing&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;join&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;return&lt;/span&gt;  &lt;span class="c1"&gt;# Just make sure the methods exists and returns nothing&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProcessTest&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;setup_method&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;b_mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProcMock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;b.B&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;b_mock&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Get the port A is listening on&lt;/span&gt;
        &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you’ve seen, process testing is really not as simple as unit testing. But I
always found bugs with it that my unit tests coudn’t detect. If you cover all
communication sequences for a process in a process test, you can be pretty
sure, that it will also work flawlessly in the final&amp;nbsp;application.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="system-testing"&gt;
&lt;h2&gt;System&amp;nbsp;Testing&lt;/h2&gt;
&lt;p&gt;If your application consists of more than one process, you still need to test
whether all processes work nicely together or not. This is something you cannot
simulate reliably with a process tests, as much as unit tests can’t replace the
process&amp;nbsp;test.&lt;/p&gt;
&lt;p&gt;Writing a good system test is very application-specific and can, depending on
the complexity of your application, be very hard or very easy. Fortunately, the
latter is the case for our ping-pong app. We just start it and copy its output
to a file. If the output is not what we expected, we modify the file
accordingly. In our test, we can now simply invoke our programm again, capture
its output and compare it to the contents of the file we created&amp;nbsp;before:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# test/system/test_pongproc.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os.path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_pongproc&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;data&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pongproc.out&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pongproc.py&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                     &lt;span class="n"&gt;universal_newlines&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your application was a server, another way of doing the system test would be
to emulate a client that speaks with it. Your system test would then be very
similar to your process tests, except that you only mimic the client and not
all processes your main process communicates&amp;nbsp;with.&lt;/p&gt;
&lt;p&gt;Other applications (like, for instance, simulations) might create a database
containing collected data. Here, you might check if these results match your&amp;nbsp;expectations.&lt;/p&gt;
&lt;p&gt;Of course you can also combine these possibilities or do something completely
different&amp;nbsp;…&lt;/p&gt;
&lt;/section&gt;
&lt;section id="my-test-are-now-running-sooo-slow"&gt;
&lt;h2&gt;“My Test Are now Running sooo&amp;nbsp;Slow!”&lt;/h2&gt;
&lt;p&gt;System and process tests often run much slower than simple unit tests, so you
may want to skip them most of the time. Pytest allows you to &lt;a href="http://pytest.org/latest/example/markers.html#mark-examples"&gt;mark&lt;/a&gt; a test with a
given name. You can then (de)select tests based on their mark when you invoke&amp;nbsp;pytest.&lt;/p&gt;
&lt;p&gt;To mark a module e.g. as &lt;code&gt;process&lt;/code&gt; test, just put a line &lt;code&gt;pytestmark =
pytest.mark.process&lt;/code&gt; somewhere in it. Likewise, you can add a &lt;code&gt;pytestmark =
pytest.mark.system&lt;/code&gt; to mark a module as system&amp;nbsp;test.&lt;/p&gt;
&lt;p&gt;You can now deselect process and system&amp;nbsp;tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;py.test&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;not (process or system)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can put this into a &lt;em&gt;pytest.ini&lt;/em&gt; as a default setting. To override this
again, use &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;True&lt;/code&gt; as selection&amp;nbsp;expression:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;py.test&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Process and system testing were the last two topics I wanted to cover in this
series. Compared to simple unit tests, they require a bit more effort. I think
they are definitely worth the extra work since they give you a lot more
confidence that your program actually works, because they are much more
realistic than unit tests can&amp;nbsp;be.&lt;/p&gt;
&lt;p&gt;In the end, these articles became much longer and more elaborate then I
originally planned them to be. However, I hope they provided a good overview
about how to design and test applications with PyZMQ and I hope that you now
run into much less problems than I did when I first started working with&amp;nbsp;PyZMQ.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2012"></category><category term="python"></category><category term="zeromq"></category><category term="pyzmq"></category><category term="testing"></category><category term="pytest"></category></entry><entry><title>Designing and Testing PyZMQ Applications – Part 2</title><link href="https://stefan.sofa-rockers.org/2012/02/07/designing-and-testing-pyzmq-applications-part-2/" rel="alternate"></link><published>2012-02-07T18:54:47+01:00</published><updated>2012-02-07T18:54:47+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-02-07:/2012/02/07/designing-and-testing-pyzmq-applications-part-2/</id><summary type="html">&lt;p&gt;This is the second part of the series &lt;em&gt;Designing and Testing PyZMQ
Applications&lt;/em&gt;. In &lt;a href="/2012/02/01/designing-and-testing-pyzmq-applications-part-1/"&gt;the first part&lt;/a&gt;,  I wrote about designing
a PyZMQ application, so this time it’s all …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is the second part of the series &lt;em&gt;Designing and Testing PyZMQ
Applications&lt;/em&gt;. In &lt;a href="/2012/02/01/designing-and-testing-pyzmq-applications-part-1/"&gt;the first part&lt;/a&gt;,  I wrote about designing
a PyZMQ application, so this time it’s all about (unit) testing (remember, if
it’s not tested, it’s broken). I also updated the &lt;a href="https://bitbucket.org/sscherfke/pyzmq-article"&gt;repository for this article&lt;/a&gt; with the new code&amp;nbsp;examples.&lt;/p&gt;
&lt;p&gt;My favorite testing tools are &lt;a href="http://pytest.org/latest/"&gt;pytest&lt;/a&gt; by Holger
Krekel and &lt;a href="http://www.voidspace.org.uk/python/mock/"&gt;Mock&lt;/a&gt; by Michael Ford.
Pytest is particularly awesome because of its re-evaluation of &lt;code&gt;assert&lt;/code&gt;
statements. If your test contains an &lt;code&gt;assert spam == 'eggs'&lt;/code&gt; and the assert
fails, pytest re-evaluates it and prints the value of &lt;code&gt;spam&lt;/code&gt;. Really helpful
and you don’t need any boilerplate code for that. Mock is really nice for
mocking external dependencies and asserting that your code called them in the
correct&amp;nbsp;way.&lt;/p&gt;
&lt;p&gt;If you cloned the &lt;a href="https://bitbucket.org/sscherfke/pyzmq-article"&gt;repository for this article&lt;/a&gt;, just run &lt;code&gt;py.test&lt;/code&gt; from its root&amp;nbsp;directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pytest&lt;span class="w"&gt; &lt;/span&gt;mock
...
Successfully&lt;span class="w"&gt; &lt;/span&gt;installed&lt;span class="w"&gt; &lt;/span&gt;pytest&lt;span class="w"&gt; &lt;/span&gt;mock
Cleaning&lt;span class="w"&gt; &lt;/span&gt;up...
$&lt;span class="w"&gt; &lt;/span&gt;py.test
&lt;span class="o"&gt;===================&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;session&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;starts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;====================&lt;/span&gt;
platform&lt;span class="w"&gt; &lt;/span&gt;darwin&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;Python&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.2.2&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;pytest-2.2.3
collected&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;items

example_app/test/test_base.py&lt;span class="w"&gt; &lt;/span&gt;....
example_app/test/test_pongproc.py&lt;span class="w"&gt; &lt;/span&gt;.......

&lt;span class="o"&gt;================&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;passed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.12&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=================&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;section id="unit-testing"&gt;
&lt;h2&gt;Unit&amp;nbsp;Testing&lt;/h2&gt;
&lt;p&gt;The probability that PyZMQ works correctly is very high. The probability that
your code will call a PyZMQ function in such a way that it blocks forever and
halts your test runner is also very high. Therefore, it’s a good idea to mock
everything PyZMQ-related for your unit tests. And since your application logic
might also not be implemented when you start testing your process, you should
mock that,&amp;nbsp;too.&lt;/p&gt;
&lt;p&gt;What you’ll actually end up testing is the&amp;nbsp;following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does your message handler call your application logic in the right way given
a certain input&amp;nbsp;message?&lt;/li&gt;
&lt;li&gt;Does your message handler create and send the correct reply based on the
return value of your application&amp;nbsp;logic?&lt;/li&gt;
&lt;/ul&gt;
&lt;section id="zmqprocess"&gt;
&lt;h3&gt;ZmqProcess&lt;/h3&gt;
&lt;p&gt;Let’s start with &lt;code&gt;ZmqProcess&lt;/code&gt; again. After all, everything else depends on it.
Testing its &lt;em&gt;setup&lt;/em&gt; method is easy. We just check that it creates a &lt;em&gt;context&lt;/em&gt;
and a &lt;em&gt;loop&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_zmqproc.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zmq.eventloop&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mock&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmqproc&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestZmqProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Tests for :class:`base.ZmqProcess`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_setup&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;zp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZmqProcess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IOLoop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Testing &lt;em&gt;stream&lt;/em&gt; is more complicated. We need to test if it can handle various
address formats, if it creates or binds correctly and if it performs a default
subscription for &lt;em&gt;&lt;span class="caps"&gt;SUB&lt;/span&gt;&lt;/em&gt;&amp;nbsp;sockets.&lt;/p&gt;
&lt;p&gt;Pytest 2.2 introduced a &lt;em&gt;parametrize&lt;/em&gt; decorator, that helps calling a test
multiple times with varying inputs. You just define one or more arguments for
your test function and a list of values for these arguments. For &lt;em&gt;test_stream&lt;/em&gt;,
I only need a &lt;em&gt;kwargs&lt;/em&gt; parameter containing the parameters for the &lt;em&gt;stream&lt;/em&gt;&amp;nbsp;call:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_zmqproc.py&lt;/span&gt;

    &lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;kwargs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1:1234&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ohai&amp;#39;&lt;/span&gt;&lt;span class="p"&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;test_stream&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;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next step is to create an instance of &lt;em&gt;ZmqProcess&lt;/em&gt; and patch some of its
attributes. We also need to set a defined return value for the socket’s
&lt;em&gt;bind_to_random_port&lt;/em&gt;&amp;nbsp;method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_zmqproc.py&lt;/span&gt;

        &lt;span class="n"&gt;zp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZmqProcess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Patch the ZmqProcess instance&lt;/span&gt;
        &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IOLoop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sock_mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;
        &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_to_random_port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the actual test, we also need to patch &lt;em&gt;ZMQStream&lt;/em&gt;. Although &lt;em&gt;mock.patch&lt;/em&gt;
could work as a function decorator, we need to use it as context processor if
we also uses pytest funcargs (e.g., via the &lt;em&gt;parametrize&lt;/em&gt; decorator—I don’t
know if it’s even possible to uses both, &lt;em&gt;mock.patch&lt;/em&gt; as decorator and pytest
funcargs in one&amp;nbsp;test).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_zmqproc.py&lt;/span&gt;

        &lt;span class="c1"&gt;# Patch ZMQStream and start testing&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;zmq.eventloop.zmqstream.ZMQStream&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;zmqstream_mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, we can check the return values of our &lt;em&gt;stream&lt;/em&gt; method and it made the
correct calls to create the&amp;nbsp;stream:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_zmqproc.py&lt;/span&gt;

            &lt;span class="c1"&gt;# Assert that the return values are correct&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;zmqstream_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_to_random_port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;

            &lt;span class="c1"&gt;# Check that the socket was crated correctly&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sock_type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],),&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;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bind&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bind&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_to_random_port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;addr&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

            &lt;span class="c1"&gt;# Check creation of the stream&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;zmqstream_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;zmqstream_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_recv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;callback&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

            &lt;span class="c1"&gt;# Check default subscribtion&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;subscribe&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sock_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;subscribe&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Note: You may have noticed that I use&lt;/em&gt; &lt;code&gt;assert my_mock.call_args == ...&lt;/code&gt;
&lt;em&gt;rather than&lt;/em&gt; &lt;code&gt;my_mock.assert_called_with(...)&lt;/code&gt;. &lt;em&gt;The reason for that is
simply, that assert statements are highlighted but ordinary function calls are
not. This makes it easier for me to find all assertions in a&amp;nbsp;test.&lt;/em&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="messagehandler"&gt;
&lt;h3&gt;MessageHandler&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;MessageHandler&lt;/em&gt; base class has only one methd, &lt;em&gt;__call__&lt;/em&gt;, but I split the
test for it into two methods—one that tests the &lt;span class="caps"&gt;JSON&lt;/span&gt;-loading functionality and
one that checks if the correct handler method is&amp;nbsp;called:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_base.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Tests for :class:`base.TestMessageHandler`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parametrize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;idx&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;msg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;test&amp;quot;, null]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;test&amp;quot;, &amp;quot;spam&amp;quot;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;test&amp;quot;]23spam&amp;#39;&lt;/span&gt;&lt;span class="p"&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;test_call_json_load&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;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&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="n"&gt;mh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parametrize&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;msg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;test&amp;quot;, &amp;quot;spam&amp;quot;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;_test&amp;quot;, &amp;quot;spam&amp;quot;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;spam&amp;quot;, &amp;quot;spam&amp;quot;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;eggs&amp;quot;, &amp;quot;spam&amp;quot;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&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;test_call_get_handler&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;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;
        &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raises&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="pongproc"&gt;
&lt;h3&gt;PongProc&lt;/h3&gt;
&lt;p&gt;Testing the &lt;em&gt;PongProc&lt;/em&gt; is not much different from testing its base class.
&lt;em&gt;pytest_funcarg__pp&lt;/em&gt; will instantiate a &lt;em&gt;PongProc&lt;/em&gt; instance for each test that
has a &lt;code&gt;pp&lt;/code&gt; argument. The tests for &lt;em&gt;setup&lt;/em&gt;, &lt;em&gt;run&lt;/em&gt; and &lt;em&gt;stop&lt;/em&gt; are easy to do.
We create a few mocks and then ask them if the tested function called them&amp;nbsp;correctly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_pongproc.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zmq.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jsonapi&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pongproc&lt;/span&gt;

&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5678&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_funcarg__pp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Creates a PongProc instance.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PongProc&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&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;TestPongProc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Tests :class:`pongproc.PongProc`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_setup&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;pp&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;make_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;make_stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;base.ZmqProcess.setup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;setup_mock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;setup_mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args_list&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;

        &lt;span class="c1"&gt;# Test if the message handler was configured correctly&lt;/span&gt;
        &lt;span class="n"&gt;rsh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_recv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Get the msg handler&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rep_stream&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_run&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;pp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_stop&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;pp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="repstreamhandler"&gt;
&lt;h3&gt;RepStreamHandler&lt;/h3&gt;
&lt;p&gt;Testing the actual message handler requires some mocks, but is apart from that
straight forward. A &lt;em&gt;funcarg&lt;/em&gt; method creates an instance of the message handler
for each test case which we feed with a message. We than check if the
application logic was called correctly and/or if a correct reply is&amp;nbsp;sent:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_pongproc.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_funcarg__rsh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Creates a RepStreamHandler instance.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RepStreamHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;ping_handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PingHandler&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;TestRepStreamHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&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;test_ping&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;rsh&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ping&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;retval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;spam&amp;#39;&lt;/span&gt;
        &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ping_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec_set&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PingHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ping_handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_pong&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retval&lt;/span&gt;

        &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ping_handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_pong&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rep_stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_args&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;retval&lt;/span&gt;&lt;span class="p"&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;test_plzdiekthybye&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;rsh&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[&amp;quot;plzdiekthxbye&amp;quot;, null]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;rsh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call_count&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="pinghandler"&gt;
&lt;h3&gt;PingHandler&lt;/h3&gt;
&lt;p&gt;When we are done with all that network stuff, we can finally test the
application logic. Easy-peasy in our&amp;nbsp;case:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/test/test_pongproc.py&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pytest_funcarg__ph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Creates a PingHandler instance.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pongproc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PingHandler&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;TestPingHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&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;test_make_pong&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;ph&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ping_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
        &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_pong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ping_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping_num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Thanks to the &lt;a href="http://www.voidspace.org.uk/python/mock/"&gt;Mock&lt;/a&gt; library, unit
testing PyZMQ apps is really not that hard and not much different from normal
unit testing. However, what we know now is only, that our process should work
&lt;em&gt;in theory&lt;/em&gt;. We haven’t yet started it and sent real messages to&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;The &lt;a href="/2012/02/15/designing-and-testing-pyzmq-applications-part-3/"&gt;next and final part&lt;/a&gt; of this series
will show you how you can automate testing complete processes. Until then, you
should get your &lt;a href="http://pypi.python.org/pypi/pytest-cov"&gt;test coverage&lt;/a&gt; up
to 100% to protect yourself from nasty surprises when you start with process&amp;nbsp;testing.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2012"></category><category term="python"></category><category term="zeromq"></category><category term="pyzmq"></category><category term="testing"></category><category term="pytest"></category><category term="mock"></category></entry><entry><title>Designing and Testing PyZMQ Applications – Part 1</title><link href="https://stefan.sofa-rockers.org/2012/02/01/designing-and-testing-pyzmq-applications-part-1/" rel="alternate"></link><published>2012-02-01T08:05:33+01:00</published><updated>2012-02-01T08:05:33+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2012-02-01:/2012/02/01/designing-and-testing-pyzmq-applications-part-1/</id><summary type="html">&lt;p&gt;&lt;a href="http://www.zeromq.org/"&gt;ZeroMQ&lt;/a&gt; (or ØMQ or &lt;span class="caps"&gt;ZMQ&lt;/span&gt;) is an intelligent messaging
framework and &lt;a href="http://zguide.zeromq.org/page:all#Fixing-the-World"&gt;described&lt;/a&gt;
as “sockets on steroids”. That is, they look like normal &lt;span class="caps"&gt;TCP&lt;/span&gt; sockets but
actually work as you’d …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://www.zeromq.org/"&gt;ZeroMQ&lt;/a&gt; (or ØMQ or &lt;span class="caps"&gt;ZMQ&lt;/span&gt;) is an intelligent messaging
framework and &lt;a href="http://zguide.zeromq.org/page:all#Fixing-the-World"&gt;described&lt;/a&gt;
as “sockets on steroids”. That is, they look like normal &lt;span class="caps"&gt;TCP&lt;/span&gt; sockets but
actually work as you’d expect sockets to work. &lt;a href="http://www.zeromq.org/bindings:python"&gt;PyZMQ&lt;/a&gt; adds even more convenience to them,
which makes it a really a good choice if you want to implement a distributed
application. Another big plus for ØMQ is that you can integrate sub-systems
written in C, Java or any other language ØMQ supports (which are a&amp;nbsp;lot).&lt;/p&gt;
&lt;p&gt;If you’ve never heard of ØMQ before, I recommend to read &lt;a href="http://nichol.as/zeromq-an-introduction"&gt;ZeroMQ an
Introduction&lt;/a&gt; by Nicholas Piël,
before you go on with this&amp;nbsp;article.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://zguide.zeromq.org/page:all"&gt;ØMQ Guide&lt;/a&gt; and &lt;a href="http://zeromq.github.com/pyzmq/"&gt;PyZMQ’s documentation&lt;/a&gt; are really good, so you can easily get
started. However, when we began to implement a larger application with it (a
distributed simulation framework), several questions arose which were not
covered by the&amp;nbsp;documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What’s the best way do design our&amp;nbsp;application?&lt;/li&gt;
&lt;li&gt;How can we keep it readable, flexible and&amp;nbsp;maintainable?&lt;/li&gt;
&lt;li&gt;How do we test&amp;nbsp;it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn’t find something like a best practice article that answered my
questions. So in this series of articles, I’m going to talk about what I’ve
learned during the last months. I’m not a PyZMQ expert (yet ;-)), but what I’ve
done so far works quite well and I never had more tests in a project than I do
have&amp;nbsp;now.&lt;/p&gt;
&lt;p&gt;You’ll find the source for the examples at &lt;a href="https://bitbucket.org/sscherfke/pyzmq-article"&gt;bitbucket&lt;/a&gt;. They are written in Python
3.2 and tested under Mac &lt;span class="caps"&gt;OS&lt;/span&gt; X Lion, Ubuntu 11.10 and Windows 7, 64 bit in each
case.  If you have any suggestions or improvements, please fork me or just
leave a &lt;a href="#comments"&gt;comment&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this first article, I’m going to talk a bit about how you could generally
design your application to be flexible, maintainable and testable. The second
part will be about unit testing and the finally, I’ll cover process and system&amp;nbsp;testing.&lt;/p&gt;
&lt;section id="comparison-of-different-approaches"&gt;
&lt;h2&gt;Comparison of Different&amp;nbsp;Approaches&lt;/h2&gt;
&lt;p&gt;There are basically three possible ways to implement a PyZMQ application. One,
that’s easy, but limited in practical use, one that’s more flexible, but not
really pythonic and one, that needs a bit more setup, but is flexible and&amp;nbsp;pythonic.&lt;/p&gt;
&lt;p&gt;All three examples feature a simple ping process and a pong process with
varying complexity. I use multiprocessing to run the pong process, because
that’s what you should usually do in real PyZMQ applications (you don’t want to
use threads and if both processes are running on the same machine, there’s no
need to invoke both of them&amp;nbsp;separately).&lt;/p&gt;
&lt;p&gt;All of the examples will have the following&amp;nbsp;output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;zmq&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;blocking_recv.py
Pong&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;request:&lt;span class="w"&gt; &lt;/span&gt;ping&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Ping&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;reply:&lt;span class="w"&gt; &lt;/span&gt;pong&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
...
Pong&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;request:&lt;span class="w"&gt; &lt;/span&gt;ping&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
Ping&lt;span class="w"&gt; &lt;/span&gt;got&lt;span class="w"&gt; &lt;/span&gt;reply:&lt;span class="w"&gt; &lt;/span&gt;pong&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s start with the easy one first. You just use on of the socket’s recv
methods in a&amp;nbsp;loop:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# blocking_recv.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;multiprocessing&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;


&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;tcp://127.0.0.1:5678&amp;#39;&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sends ping requests and waits for replies.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ping &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_unicode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# This blocks until we get something&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Ping got reply:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rep&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;pong&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Waits for ping requests and replies with a pong.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_unicode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# This also blocks&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pong got request:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;pong_proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multiprocessing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&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="n"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pong_proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;pong_proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So this is very easy and no that much code. The problem with this is, that it
only works well if your process only uses one socket. Unfortunately, in larger
applications that is rather rarely the&amp;nbsp;case.&lt;/p&gt;
&lt;p&gt;A way to handle multiple sockets per process is polling. In addition to your
context and socket(s), you need a &lt;em&gt;poller&lt;/em&gt;. You also have to tell it which
events on which socket you are going to&amp;nbsp;poll:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# polling.py&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pong&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Waits for ping requests and replies with a pong.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create a poller and register the events we want to poll&lt;/span&gt;
    &lt;span class="n"&gt;poller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Poller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;poller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POLLIN&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POLLOUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Get all sockets that can do something&lt;/span&gt;
        &lt;span class="n"&gt;socks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="c1"&gt;# Check if we can receive something&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;socks&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;socks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POLLIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv_unicode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pong got request:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Check if we cann send something&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;socks&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;socks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POLLOUT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;poller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unregister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You see, that our &lt;em&gt;pong&lt;/em&gt; function got pretty ugly. You need 10 iterations to do
five ping-pongs, because in each iteration you can either send or reply. And
each socket you add to your process adds two more if-statements. You could
improve that design if you created a base class wrapping the polling loop and
just register sockets and callbacks in an inheriting&amp;nbsp;class.&lt;/p&gt;
&lt;p&gt;That brings us to our final example. PyZMQ comes with with an &lt;a href="http://zeromq.github.com/pyzmq/eventloop.html"&gt;adapted Tornado
eventloop&lt;/a&gt; that handles the
polling and works with &lt;a href="http://zeromq.github.com/pyzmq/api/generated/zmq.eventloop.zmqstream.html#zmq.eventloop.zmqstream.ZMQStream"&gt;ZMQStreams&lt;/a&gt;,
that wrap sockets and add some&amp;nbsp;functionality:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# eventloop.py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zmq.eventloop&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zmqstream&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiprocessing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Waits for ping requests and replies with a pong.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Initializes the event loop, creates the sockets/streams and&lt;/span&gt;
&lt;span class="sd"&gt;        starts the (blocking) loop.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IOLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# This is the event loop&lt;/span&gt;

        &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# We need to create a stream from our socket and&lt;/span&gt;
        &lt;span class="c1"&gt;# register a callback for recv events.&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmqstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZMQStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_ping&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Start the loop. It runs until we stop it.&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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;handle_ping&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;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Handles ping requests and sends back a pong.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# req is a list of byte objects&lt;/span&gt;
        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pong got request:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_unicode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# We’ll stop the loop after 5 pings&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This even adds more boilerplate code, but it will pay of if you use more
sockets and most of that stuff in &lt;em&gt;run()&lt;/em&gt; can be put into a base class. Another
drawback is, that the IOLoop only uses &lt;a href="http://zeromq.github.com/pyzmq/api/generated/zmq.core.socket.html#zmq.core.socket.Socket.recv_multipart"&gt;recv_multipart()&lt;/a&gt;.
So you always get a lists of byte strings which you have to decode or
deserialize on your own. However, you can use all the &lt;em&gt;send&lt;/em&gt; methods socket
offers (like &lt;em&gt;send_unicode()&lt;/em&gt; or &lt;em&gt;send_json()&lt;/em&gt;). You can also stop the loop
from within a message&amp;nbsp;handler.&lt;/p&gt;
&lt;p&gt;In the next sections, I’ll discuss how you could implement a PyZMQ process that
uses the event&amp;nbsp;loop.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="communication-design"&gt;
&lt;h2&gt;Communication&amp;nbsp;Design&lt;/h2&gt;
&lt;p&gt;Before you start to implement anything, you should think about what kind of
processes you need in your application and which messages they exchange. You
should also decide what kind of message format and serialization you want to&amp;nbsp;use.&lt;/p&gt;
&lt;p&gt;PyZMQ has built-in support for Unicode (&lt;em&gt;send&lt;/em&gt; sends plain C strings which map
to Python &lt;em&gt;byte&lt;/em&gt; objects, so there’s a separate method to send Unicode
strings), &lt;span class="caps"&gt;JSON&lt;/span&gt; and&amp;nbsp;Pickle.&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;JSON&lt;/span&gt; is nice, because it’s fast and lets you integrate processes written in
other languages into you application. It’s also a bit safer, because you cannot
receive arbitrary objects as with &lt;a href="http://docs.python.org/py3k/library/pickle#restricting-globals"&gt;pickle&lt;/a&gt;.  The most
straightforward syntax for &lt;span class="caps"&gt;JSON&lt;/span&gt; messages is to let them be triples &lt;em&gt;[msg_type,
args, kwargs]&lt;/em&gt;, where &lt;em&gt;msg_type&lt;/em&gt; maps to a method name and &lt;em&gt;args&lt;/em&gt; and &lt;em&gt;kwargs&lt;/em&gt;
get passed as positional and keyword&amp;nbsp;arguments.&lt;/p&gt;
&lt;p&gt;I strongly recommend you to document each chain of messages your application
sends to perform a certain task. I do this with fancy PowerPoint graphics and
with even fancier &lt;span class="caps"&gt;ASCII&lt;/span&gt; art in &lt;a href="http://sphinx.pocoo.org"&gt;Sphinx&lt;/a&gt;. Here is how
I would document our&amp;nbsp;ping-pong:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;Sending pings&lt;/span&gt;
&lt;span class="gh"&gt;-------------&lt;/span&gt;

&lt;span class="m"&gt;*&lt;/span&gt; If the ping process sends a &lt;span class="ge"&gt;*ping*&lt;/span&gt;, the pong processes responds with a
  &lt;span class="ge"&gt;*pong*&lt;/span&gt;.
&lt;span class="m"&gt;*&lt;/span&gt; The number of pings (and pongs) is counted. The current ping count is
  sent with each message.

&lt;span class="se"&gt;::&lt;/span&gt;

&lt;span class="s"&gt;    PingProc      PongProc&lt;/span&gt;
&lt;span class="s"&gt;     [REQ] ---1--&amp;gt; [REP]&lt;/span&gt;
&lt;span class="s"&gt;           &amp;lt;--2---&lt;/span&gt;


&lt;span class="s"&gt;    1 IN : [&amp;#39;ping, count&amp;#39;]&lt;/span&gt;
&lt;span class="s"&gt;    1 OUT: [&amp;#39;ping, count&amp;#39;]&lt;/span&gt;

&lt;span class="s"&gt;    2 IN : [&amp;#39;pong, count&amp;#39;]&lt;/span&gt;
&lt;span class="s"&gt;    2 OUT: [&amp;#39;pong, count&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First, I write some bullet points that explain how the processes behave and why
they behave this way. This is followed by some kind of sequence diagram that
shows when which process sents which message using which socket type. Finally,
I write down how the messages are looking. &lt;code&gt;# IN&lt;/code&gt; is what you would pass to
&lt;em&gt;send_multipart&lt;/em&gt; and &lt;code&gt;# OUT&lt;/code&gt; is, what is received on the other side by
&lt;em&gt;recv_multipart&lt;/em&gt;. If one of the participating sockets is a &lt;em&gt;&lt;span class="caps"&gt;ROUTER&lt;/span&gt;&lt;/em&gt; or
&lt;em&gt;&lt;span class="caps"&gt;DEALER&lt;/span&gt;&lt;/em&gt;, &lt;em&gt;&lt;span class="caps"&gt;IN&lt;/span&gt;&lt;/em&gt; and &lt;em&gt;&lt;span class="caps"&gt;OUT&lt;/span&gt;&lt;/em&gt; will differ (though that’s not the case in this
example). Everything in single quotation marks (&lt;code&gt;'&lt;/code&gt;) represents a &lt;span class="caps"&gt;JSON&lt;/span&gt;
serialized&amp;nbsp;list.&lt;/p&gt;
&lt;p&gt;If our pong process used a &lt;em&gt;&lt;span class="caps"&gt;ROUTER&lt;/span&gt;&lt;/em&gt; socket instead of the &lt;em&gt;&lt;span class="caps"&gt;REP&lt;/span&gt;&lt;/em&gt; socket, it
would look like&amp;nbsp;this:&lt;/p&gt;
&lt;pre&gt;1 IN : ['ping, count']
1 OUT: [ping_uuid, '', 'ping, count']

2 IN : [ping_uuid, '', 'pong, count']
2 OUT: ['pong, count']&lt;/pre&gt;
&lt;p&gt;This seems like a lot of tedious work, but trust me, it really helps a lot
when you need to change something a few weeks&amp;nbsp;later!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="application-design"&gt;
&lt;h2&gt;Application&amp;nbsp;Design&lt;/h2&gt;
&lt;p&gt;In the examples above, the &lt;em&gt;Pong&lt;/em&gt; process was responsible for setting
everything up, for receiving/sending messages and for the actual application
logic (counting incoming pings and creating a&amp;nbsp;pong).&lt;/p&gt;
&lt;p&gt;Obviously, this is not a very good design (at least if your application gets
more complex than our little ping-pong example). What we can do about this is
to put most of that nasty setup stuff into a base class which all your
processes can inherit from, separate message handling and (de)serialization
from it and finally put all the actual application logic into a separate
(PyZMQ-independent) class. This will result in a three-level&amp;nbsp;architecture:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The lowest tier will contain the entry point of the process, set-up
everything and start the event loop. A common base class provides utilities
for creating sockets/streams and setting everything&amp;nbsp;up.&lt;/li&gt;
&lt;li&gt;The second level is message handling and (de) serialization. A base class
performs the (de)serialization and error handling. A message handler
inherits this class and implements a method for each message type that
should be&amp;nbsp;handled.&lt;/li&gt;
&lt;li&gt;The third level will be the application logic and completely&amp;nbsp;PyZMQ-agnostic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Base classes should be defined for the first two tiers two reduce redundant
code in multiple processes or message handlers. The following figure shows the
five classes our process is going to consist&amp;nbsp;of:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="The architecture of our pong process." src="/images/pongproc_architecture.png" style="width: 450px" /&gt;
&lt;figcaption&gt;The refactored PongProc now consists of three layers. The main class
&lt;em&gt;PongProc&lt;/em&gt; inherits &lt;em&gt;ZMQProcess&lt;/em&gt;. Every stream gets a &lt;em&gt;MessageHandler&lt;/em&gt;. In
our example it’s just &lt;em&gt;RepStreamHandler&lt;/em&gt;. Finally, you can have one ore more
classes containing the (PyZMQ-agnostic) application logic. In our example,
it’s called &lt;em&gt;PingHandler&lt;/em&gt;, because it handles incoming pings.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;section id="zmqpocess-the-base-class-for-all-processes"&gt;
&lt;h3&gt;ZmqPocess – The Base Class for all&amp;nbsp;Processes&lt;/h3&gt;
&lt;p&gt;The base class basically implements two&amp;nbsp;things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;em&gt;setup&lt;/em&gt; method that creates a context an a&amp;nbsp;loop&lt;/li&gt;
&lt;li&gt;a &lt;em&gt;stream&lt;/em&gt; factory method for streams with a &lt;em&gt;on_recv&lt;/em&gt; callback. It creates
a socket and can connect/bind it to a given address or bind it to a random
port (that’s why it returns the port number in addition to the stream&amp;nbsp;itself).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also inherits &lt;a href="http://docs.python.org/py3k/library/multiprocessing#process-and-exceptions"&gt;multiprocessing.Process&lt;/a&gt;
so that it is easier to spawn it as sub-process. Of course, you can also just
call its &lt;em&gt;run()&lt;/em&gt; method from you &lt;em&gt;main()&lt;/em&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/base.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;multiprocessing&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zmq.eventloop&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zmqstream&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ZmqProcess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiprocessing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    This is the base for all processes and offers utility functions&lt;/span&gt;
&lt;span class="sd"&gt;    for setup and creating new streams.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;The ØMQ :class:`~zmq.Context` instance.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;PyZMQ&amp;#39;s event loop (:class:`~zmq.eventloop.ioloop.IOLoop`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Creates a :attr:`context` and an event :attr:`loop` for the process.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ioloop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IOLoop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance&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;stream&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;sock_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Creates a :class:`~zmq.eventloop.zmqstream.ZMQStream`.&lt;/span&gt;

&lt;span class="sd"&gt;        :param sock_type: The ØMQ socket type (e.g. ``zmq.REQ``)&lt;/span&gt;
&lt;span class="sd"&gt;        :param addr: Address to bind or connect to formatted as *host:port*,&lt;/span&gt;
&lt;span class="sd"&gt;                *(host, port)* or *host* (bind to random port).&lt;/span&gt;
&lt;span class="sd"&gt;                If *bind* is ``True``, *host* may be:&lt;/span&gt;

&lt;span class="sd"&gt;                - the wild-card ``*``, meaning all available interfaces,&lt;/span&gt;
&lt;span class="sd"&gt;                - the primary IPv4 address assigned to the interface, in its&lt;/span&gt;
&lt;span class="sd"&gt;                numeric representation or&lt;/span&gt;
&lt;span class="sd"&gt;                - the interface name as defined by the operating system.&lt;/span&gt;

&lt;span class="sd"&gt;                If *bind* is ``False``, *host* may be:&lt;/span&gt;

&lt;span class="sd"&gt;                - the DNS name of the peer or&lt;/span&gt;
&lt;span class="sd"&gt;                - the IPv4 address of the peer, in its numeric representation.&lt;/span&gt;

&lt;span class="sd"&gt;                If *addr* is just a host name without a port and *bind* is&lt;/span&gt;
&lt;span class="sd"&gt;                ``True``, the socket will be bound to a random port.&lt;/span&gt;
&lt;span class="sd"&gt;        :param bind: Binds to *addr* if ``True`` or tries to connect to it&lt;/span&gt;
&lt;span class="sd"&gt;                otherwise.&lt;/span&gt;
&lt;span class="sd"&gt;        :param callback: A callback for&lt;/span&gt;
&lt;span class="sd"&gt;                :meth:`~zmq.eventloop.zmqstream.ZMQStream.on_recv`, optional&lt;/span&gt;
&lt;span class="sd"&gt;        :param subscribe: Subscription pattern for *SUB* sockets, optional,&lt;/span&gt;
&lt;span class="sd"&gt;                defaults to ``b&amp;#39;&amp;#39;``.&lt;/span&gt;
&lt;span class="sd"&gt;        :returns: A tuple containg the stream and the port number.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# addr may be &amp;#39;host:port&amp;#39; or (&amp;#39;host&amp;#39;, port)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Bind/connect the socket&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bind&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;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_to_random_port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# Add a default subscription for SUB sockets&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sock_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Create the stream and add the callback&lt;/span&gt;
        &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmqstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZMQStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&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;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&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;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="pongproc-the-actual-process"&gt;
&lt;h3&gt;PongProc – The Actual&amp;nbsp;Process&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;PongProc&lt;/em&gt; inherits &lt;em&gt;ZmqProcess&lt;/em&gt; and is the main class for our process. It
creates the streams, starts the event loop and dispatches all messages to the
appropriate&amp;nbsp;handlers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/pongproc.py&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zmq&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;base&lt;/span&gt;


&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5678&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PongProc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZmqProcess&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Main processes for the Ponger. It handles ping requests and sends back&lt;/span&gt;
&lt;span class="sd"&gt;    a pong.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;bind_addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bind_addr&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ping_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PingHandler&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;setup&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sets up PyZMQ and creates all streams.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Create the stream and add the message handler&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on_recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RepStreamHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rep_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                 &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ping_handler&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;run&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Sets up everything and starts the event loop.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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;stop&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="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Stops the event loop.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are going to start this process as a sub-process via &lt;em&gt;start&lt;/em&gt;, make sure
everything you instantiate in &lt;em&gt;__init__&lt;/em&gt; is pickle-able or it won’t work on
Windows (Linux and Mac &lt;span class="caps"&gt;OS&lt;/span&gt; X use &lt;em&gt;fork&lt;/em&gt; to create a sub-process and &lt;em&gt;fork&lt;/em&gt; just
makes a copy of the main process and gives it a new process &lt;span class="caps"&gt;ID&lt;/span&gt;. &lt;a href="http://docs.python.org/py3k/library/multiprocessing#windows"&gt;On Windows&lt;/a&gt;, there is no
&lt;em&gt;fork&lt;/em&gt; and the context of your main process is pickled and sent to the&amp;nbsp;sub-process).&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;setup&lt;/em&gt;, call &lt;code&gt;super().setup()&lt;/code&gt; before you create a stream or you won’t
have a &lt;em&gt;loop&lt;/em&gt; instance for them. We call &lt;em&gt;setup&lt;/em&gt; from &lt;em&gt;run&lt;/em&gt;, because the
context must be created within the new system process, which wouldn’t be the
case if we called &lt;em&gt;setup&lt;/em&gt; from &lt;em&gt;__init__&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;stop&lt;/em&gt; method is not really necessary in this example, but it can be used
to send stop messages to sub-processes when the main process terminates and to
do other kinds of clean-up. You can also execute it if you except
a &lt;code&gt;KeyboardInterrupt&lt;/code&gt; after calling &lt;em&gt;run&lt;/em&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="messagehandler-the-base-class-for-message-handlers"&gt;
&lt;h3&gt;MessageHandler — The Base Class for Message&amp;nbsp;Handlers&lt;/h3&gt;
&lt;p&gt;A PyZMQ message handler can be any callable that accepts one argument—the list
of message parts as byte objects. Hence, our &lt;em&gt;MessageHandler&lt;/em&gt; class needs to
implement &lt;em&gt;__call__&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# exmaple_app/base.py&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zmq.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jsonapi&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Base class for message handlers for a :class:`ZMQProcess`.&lt;/span&gt;

&lt;span class="sd"&gt;    Inheriting classes only need to implement a handler function for each&lt;/span&gt;
&lt;span class="sd"&gt;    message type.&lt;/span&gt;

&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;json_load&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_json_load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_load&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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;msg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Gets called when a messages is received by the stream this handlers is&lt;/span&gt;
&lt;span class="sd"&gt;        registered at. *msg* is a list as return by&lt;/span&gt;
&lt;span class="sd"&gt;        :meth:`zmq.core.socket.Socket.recv_multipart`.&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# Try to JSON-decode the index &amp;quot;self._json_load&amp;quot; of the message&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_json_load&lt;/span&gt;
        &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;

        &lt;span class="c1"&gt;# Get the actual message handler and call it&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; starts with an &amp;quot;_&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;msg_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nb"&gt;getattr&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;msg_type&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it’s quite simle. It just tries to &lt;span class="caps"&gt;JSON&lt;/span&gt;-load the index defined
by &lt;code&gt;self._json_load&lt;/code&gt;. We earlier defined, that the first element of the
&lt;span class="caps"&gt;JSON&lt;/span&gt;-encoded message defines the message type (e.g., &lt;em&gt;ping&lt;/em&gt;). If an attribute
of the same name exists in the inheriting class, it is called with the remainer
of the&amp;nbsp;message.&lt;/p&gt;
&lt;p&gt;You can also add logging or additional security measures here, but that is not
necessary&amp;nbsp;here.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="repstreamhandler-the-concrete-message-handler"&gt;
&lt;h3&gt;RepStreamHandler — The Concrete Message&amp;nbsp;Handler&lt;/h3&gt;
&lt;p&gt;This class inherits the &lt;em&gt;MessageHandler&lt;/em&gt; I just showed you and is used in
&lt;em&gt;PongProc.setup&lt;/em&gt;. It defines a handler method for &lt;em&gt;ping&lt;/em&gt; messages and the
&lt;em&gt;plzdiekthxbye&lt;/em&gt; stop message. In its &lt;em&gt;__init__&lt;/em&gt; it receives references to the
&lt;em&gt;rep_stream&lt;/em&gt;, PongProcs &lt;em&gt;stop&lt;/em&gt; method and to the &lt;em&gt;ping_handler&lt;/em&gt;, our actual
application&amp;nbsp;logic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/pongproc.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RepStreamHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MessageHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Handels messages arrvinge at the PongProc’s REP stream.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&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;rep_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ping_handler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rep_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep_stream&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ping_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ping_handler&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping&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;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Send back a pong.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ping_handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make_pong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_rep_stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rep&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;plzdiekthxbye&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;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Just calls :meth:`PongProc.stop`.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="pinghandler-the-application-logic"&gt;
&lt;h3&gt;PingHandler – The Application&amp;nbsp;Logic&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;PingHandler&lt;/em&gt; contains the actual application logic (which is not much,
in this example). The &lt;em&gt;make_pong&lt;/em&gt; method just gets the number of pings sent
with the &lt;em&gt;ping&lt;/em&gt; message and creates a new &lt;em&gt;pong&lt;/em&gt; message. The serialization
is done by &lt;em&gt;PongProc&lt;/em&gt;, so our Handler does not depend on&amp;nbsp;PyZMQ:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# example_app/pongproc.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PingHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&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;make_pong&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;num_pings&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Creates and returns a pong message.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pong got request number &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;num_pings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pong&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_pings&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Okay, that’s it for now. I showed you three ways to use PyZMQ. If you have a
very simple process with only one socket, you can easily use its blocking
&lt;em&gt;recv&lt;/em&gt; methods. If you need more than one socket, I recommend using the event
loop. And polling … you don’t want to use&amp;nbsp;that.&lt;/p&gt;
&lt;p&gt;If you decide to use PyZMQ’s event loop, you should separate the application
logic from all the PyZMQ stuff (like creating streams, sending/receiving
messages and dispatching them). If your application consists of more then one
process (which is usually the case), you should also create a base class with
shared functionality for&amp;nbsp;them.&lt;/p&gt;
&lt;p&gt;In the &lt;a href="/2012/02/07/designing-and-testing-pyzmq-applications-part-2/"&gt;next part&lt;/a&gt;, I’m going to talk about how you can test
your&amp;nbsp;application.&lt;/p&gt;
&lt;/section&gt;
</content><category term="2012"></category><category term="python"></category><category term="zeromq"></category><category term="pyzmq"></category><category term="testing"></category><category term="pytest"></category><category term="mock"></category><category term="software architecture"></category></entry><entry><title>Book review: NumPy 1.5 Beginner’s Guide</title><link href="https://stefan.sofa-rockers.org/2011/11/21/book-review-numpy-15-beginners-guide/" rel="alternate"></link><published>2011-11-21T18:55:00+01:00</published><updated>2011-11-21T18:55:00+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2011-11-21:/2011/11/21/book-review-numpy-15-beginners-guide/</id><summary type="html">&lt;p&gt;I recently got the chance to review the book &lt;a href="http://www.packtpub.com/numpy-1-5-using-real-world-examples-beginners-guide/book"&gt;NumPy 1.5 Beginner’s Guide&lt;/a&gt;
by &lt;em&gt;Ivan Idris&lt;/em&gt; and published by &lt;a href="http://www.packtpub.com"&gt;Packt Publishing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It covers many aspects of &lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt; and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently got the chance to review the book &lt;a href="http://www.packtpub.com/numpy-1-5-using-real-world-examples-beginners-guide/book"&gt;NumPy 1.5 Beginner’s Guide&lt;/a&gt;
by &lt;em&gt;Ivan Idris&lt;/em&gt; and published by &lt;a href="http://www.packtpub.com"&gt;Packt Publishing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It covers many aspects of &lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt; and also
introduces &lt;a href="http://www.scipy.org/"&gt;SciPy&lt;/a&gt; as well as &lt;a href="http://matplotlib.sourceforge.net/"&gt;Matplotlib&lt;/a&gt;. The author includes a lot of examples
and exercises and also shows the effects of some not-so-easy-to-understand
functions using matplotlib&amp;nbsp;graphs.&lt;/p&gt;
&lt;p&gt;The book is easy to read, so you should make fast progress in learning NumPy.
Overall, it’s a good read for NumPy beginners. Advanced NumPy users, who just
want to look up how specific things work, are better of with &lt;a href="http://docs.scipy.org/doc/numpy/"&gt;NumPy’s
documentation&lt;/a&gt;,&amp;nbsp;though.&lt;/p&gt;
</content><category term="2011"></category><category term="python"></category><category term="scientific"></category><category term="numpy"></category><category term="book"></category><category term="review"></category></entry><entry><title>SimPy 2.2</title><link href="https://stefan.sofa-rockers.org/2011/09/27/simpy-22/" rel="alternate"></link><published>2011-09-27T21:42:25+02:00</published><updated>2011-09-27T21:42:25+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2011-09-27:/2011/09/27/simpy-22/</id><summary type="html">&lt;p&gt;SimPy is a process-based discrete-event simulation library written in pure&amp;nbsp;Python.&lt;/p&gt;
&lt;p&gt;Ontje Lünsdorf and I have already contributed to prior version of SimPy and
now have become members of the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;SimPy is a process-based discrete-event simulation library written in pure&amp;nbsp;Python.&lt;/p&gt;
&lt;p&gt;Ontje Lünsdorf and I have already contributed to prior version of SimPy and
now have become members of the SimPy&amp;nbsp;team.&lt;/p&gt;
&lt;p&gt;In SimPy 2.2, we have restructured the package layout to be conform to the
&lt;a href="http://guide.python-distribute.org/"&gt;Hitchhiker’s Guide to packaging&lt;/a&gt;. We
ported the the unit tests (524 test cases) to &lt;a href="http://pytest.org/"&gt;pytest&lt;/a&gt;.
We improved and cleaned up parts of the documentation and finally, we fixed the
behavior of &lt;em&gt;Store._put&lt;/em&gt;—thanks to Johannes Koomer for the heads-up and the&amp;nbsp;fix.&lt;/p&gt;
&lt;p&gt;You can download SimPy from &lt;a href="http://pypi.python.org/pypi/SimPy/2.2"&gt;PyPI&lt;/a&gt;.&lt;/p&gt;
</content><category term="2011"></category><category term="python"></category><category term="scientific"></category><category term="simpy"></category><category term="simulation"></category></entry><entry><title>Building NumPy, SciPy &amp; Matplotlib for Python 2.7 on Snow Leopard</title><link href="https://stefan.sofa-rockers.org/2010/11/17/building-numpy-scipy-matplotlib-python-27-snow-leo/" rel="alternate"></link><published>2010-11-17T08:30:39+01:00</published><updated>2010-11-17T08:30:39+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2010-11-17:/2010/11/17/building-numpy-scipy-matplotlib-python-27-snow-leo/</id><summary type="html">&lt;p&gt;&lt;a href="/2010/11/11/building-scipy-snow-leopard-python-27/"&gt;A few days ago&lt;/a&gt; I wrote about how to build SciPy for Python 2.7 on Mac &lt;span class="caps"&gt;OS&lt;/span&gt; 10.6 Snow&amp;nbsp;Leopard.&lt;/p&gt;
&lt;p&gt;Usually you want to install &lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt;, &lt;a href="http://www.scipy.org/"&gt;SciPy&lt;/a&gt; and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/2010/11/11/building-scipy-snow-leopard-python-27/"&gt;A few days ago&lt;/a&gt; I wrote about how to build SciPy for Python 2.7 on Mac &lt;span class="caps"&gt;OS&lt;/span&gt; 10.6 Snow&amp;nbsp;Leopard.&lt;/p&gt;
&lt;p&gt;Usually you want to install &lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt;, &lt;a href="http://www.scipy.org/"&gt;SciPy&lt;/a&gt; and &lt;a href="http://matplotlib.sourceforge.net/"&gt;Matplotlib&lt;/a&gt;. After reading &lt;a href="http://www.scipy.org/Installing_SciPy/Mac_OS_X"&gt;Installing SciPy/Mac &lt;span class="caps"&gt;OS&lt;/span&gt;
X&lt;/a&gt;, the &lt;a href="http://matplotlib.sourceforge.net/users/installing.html"&gt;Matplotlib
installation instructions&lt;/a&gt; and the &lt;a href="http://blog.hyperjeff.net/?p=160"&gt;HJBlog&lt;/a&gt; you might come to the conclusion, that
it’s not trivial to build them on your own and that you better use the 32bit
binaries for Python 2.6 or get them via &lt;a href="http://trac.macports.org/browser/trunk/dports/python/"&gt;MacPorts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But actually it’s really easy. The only dependencies that you need to install
are &lt;a href="http://developer.apple.com/technologies/xcode.html"&gt;Xcode&lt;/a&gt; (for gcc and
X11) and &lt;a href="http://r.research.att.com/tools/"&gt;gfortran&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To simplify the the installation, I wrote a small &lt;a href="https://stefan.sofa-rockers.org/downloads/Makefile_nsm"&gt;Makefile&lt;/a&gt; that downloads all packages (except for
Xcode) and builds/installs&amp;nbsp;them:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Rename* the downloaded makefile to &lt;code&gt;Makefile&lt;/code&gt;, open &lt;em&gt;Terminal&lt;/em&gt; and &lt;code&gt;cd&lt;/code&gt;
to the diretory with the&amp;nbsp;makefile.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download gfortran and start the graphical&amp;nbsp;installer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;fortran
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download and install NumPy, SciPy and&amp;nbsp;Matplotlib:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run NumPy’s and SciPy’s test suite and delete the temporary build&amp;nbsp;diretory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;clean
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s how the makefile looks&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c"&gt;# Download all packages to this directory.&lt;/span&gt;
&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./PYTMP

&lt;span class="c"&gt;# Select Python version.&lt;/span&gt;
&lt;span class="nv"&gt;PYVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.7
&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Package to download frm http://r.research.att.com/&lt;/span&gt;
&lt;span class="nv"&gt;FORTRANPACKAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gfortran-42-5664.pkg

&lt;span class="c"&gt;# Select which versions of the packages you want to install.&lt;/span&gt;
&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.5.0
&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.8.0
&lt;span class="nv"&gt;MATPLOTLIBMAJORVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.0
&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBMAJORVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.0

&lt;span class="c"&gt;# Normally, you shouldn’t need change this.&lt;/span&gt;
&lt;span class="nv"&gt;OSX_SDK_VER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;.6
&lt;span class="nv"&gt;ARCH_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;-arch&lt;span class="w"&gt; &lt;/span&gt;i386&lt;span class="w"&gt; &lt;/span&gt;-arch&lt;span class="w"&gt; &lt;/span&gt;x86_64

&lt;span class="c"&gt;# Values for some environment variables. You shouldn’t need to change this.&lt;/span&gt;
&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OSX_SDK_VER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -I/usr/X11/include -I/usr/X11/include/freetype2 -isysroot /Developer/SDKs/MacOSX&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OSX_SDK_VER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.sdk&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-Wall -undefined dynamic_lookup -bundle &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -L/usr/X11/lib -syslibroot,/Developer/SDKs/MacOSX&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OSX_SDK_VER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.sdk&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH_FLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;


&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;

&lt;span class="c"&gt;# Create a temporary directory for the build process&lt;/span&gt;
&lt;span class="nf"&gt;_tmp_dir&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Clean the the temporary build directory&lt;/span&gt;
&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Download gfortran and start the installer&lt;/span&gt;
&lt;span class="nf"&gt;fortran&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_tmp_dir&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import urllib; urllib.urlretrieve(&amp;#39;http://r.research.att.com/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORTRANPACKAGE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORTRANPACKAGE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;open&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FORTRANPACKAGE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Download all required packages&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_tmp_dir&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import urllib; urllib.urlretrieve(&amp;#39;http://sourceforge.net/projects/numpy/files/NumPy/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/numpy-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz/download&amp;#39;, &amp;#39;numpy-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import urllib; urllib.urlretrieve(&amp;#39;http://sourceforge.net/projects/scipy/files/scipy/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/scipy-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz/download&amp;#39;, &amp;#39;scipy-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import urllib; urllib.urlretrieve(&amp;#39;http://sourceforge.net/projects/matplotlib/files/matplotlib/matplotlib-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBMAJORVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/matplotlib-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz/download&amp;#39;, &amp;#39;matplotlib-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz&amp;#39;)&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Extract, build and install NumPy&lt;/span&gt;
&lt;span class="nf"&gt;numpy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;numpy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;numpy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.tar.gz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;numpy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NUMPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;..&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import numpy; print &amp;#39;Installed NumPy&amp;#39;, numpy.__version__&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Extract, build and install SciPy&lt;/span&gt;
&lt;span class="nf"&gt;scipy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;scipy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;scipy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.tar.gz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;scipy-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SCIPYVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;..&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import scipy; print &amp;#39;Installed SciPy&amp;#39;, scipy.__version__&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Extract, build and install Matplotlib&lt;/span&gt;
&lt;span class="nf"&gt;matplotlib&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;-rf&lt;span class="w"&gt; &lt;/span&gt;matplotlib-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;matplotlib-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.tar.gz&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;matplotlib-&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MATPLOTLIBVERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;..&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import matplotlib; print &amp;#39;Installed Matplotlib&amp;#39;, matplotlib.__version__&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Fetch and build NumPy, SciPy and Matplotlib&lt;/span&gt;
&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="n"&gt;scipy&lt;/span&gt; &lt;span class="n"&gt;matplotlib&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;all done&amp;quot;&lt;/span&gt;

&lt;span class="c"&gt;# Execute tests for NumPy and SciPy&lt;/span&gt;
&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_tmp_dir&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TMP_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import numpy; numpy.test(&amp;#39;1&amp;#39;, &amp;#39;10&amp;#39;)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import scipy; scipy.test(&amp;#39;1&amp;#39;, &amp;#39;10&amp;#39;)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have only tested it on my local machine yet. Hope, it helps and you don’t run
into any&amp;nbsp;troubles.&lt;/p&gt;
&lt;p&gt;* If you don’t rename the makefile, you must pass it’s name to &lt;em&gt;make:&lt;/em&gt; &lt;code&gt;make
-f Makefile_nsm &amp;lt;target&amp;gt;&lt;/code&gt;.&lt;/p&gt;
</content><category term="2010"></category><category term="python"></category><category term="scientific"></category><category term="numpy"></category><category term="scipy"></category><category term="matplotlib"></category><category term="os x"></category></entry><entry><title>Building SciPy on Snow Leopard with Python 2.7</title><link href="https://stefan.sofa-rockers.org/2010/11/11/building-scipy-snow-leopard-python-27/" rel="alternate"></link><published>2010-11-11T09:30:08+01:00</published><updated>2010-11-11T09:30:08+01:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2010-11-11:/2010/11/11/building-scipy-snow-leopard-python-27/</id><summary type="html">&lt;p&gt;I recently had some struggle to build and install &lt;em&gt;SciPy 0.8.0&lt;/em&gt;  on &lt;em&gt;Mac &lt;span class="caps"&gt;OS&lt;/span&gt;
X 10.6 Snow Leopard&lt;/em&gt;, but actually it’s quite&amp;nbsp;easy.&lt;/p&gt;
&lt;p&gt;I used &lt;a href="http://www.scipy.org/Installing_SciPy/Mac_OS_X"&gt;the …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently had some struggle to build and install &lt;em&gt;SciPy 0.8.0&lt;/em&gt;  on &lt;em&gt;Mac &lt;span class="caps"&gt;OS&lt;/span&gt;
X 10.6 Snow Leopard&lt;/em&gt;, but actually it’s quite&amp;nbsp;easy.&lt;/p&gt;
&lt;p&gt;I used &lt;a href="http://www.scipy.org/Installing_SciPy/Mac_OS_X"&gt;the instructions on scipy.org&lt;/a&gt; and &lt;a href="http://blog.hyperjeff.net/?p=160"&gt;the HJBlog&lt;/a&gt; as source. Since there are builds of
&lt;em&gt;NumPy 1.5.0&lt;/em&gt; for &lt;em&gt;Python 2.7&lt;/em&gt; &lt;a href="http://sourceforge.net/projects/numpy/files/"&gt;available&lt;/a&gt;, you don’t need to install
&lt;em&gt;fftw&lt;/em&gt; and &lt;em&gt;umfpack&lt;/em&gt;&amp;nbsp;manually.&lt;/p&gt;
&lt;p&gt;You only need to install &lt;em&gt;gfortran&lt;/em&gt; from &lt;a href="http://r.research.att.com/tools/"&gt;this site&lt;/a&gt;. Pick the latest build for &lt;em&gt;Snow Leopard&lt;/em&gt;
(e.g. &lt;a href="http://r.research.att.com/gfortran-42-5664.pkg"&gt;this one&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Next, install &lt;em&gt;NumPy&lt;/em&gt; with the disk image from SourceForge or with &lt;em&gt;pip&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;numpy
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To build and install &lt;em&gt;SciPy&lt;/em&gt;, download the latest version from &lt;a href="http://sourceforge.net/projects/scipy/files/"&gt;SourceForge&lt;/a&gt; and do the&amp;nbsp;following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;scipy-0.8.0.tar.gz
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;scipy-0.8.0
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-arch i386 -arch x86_64&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-Wall -undefined dynamic_lookup -bundle -arch i386 -arch x86_64&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-arch i386 -arch x86_64&amp;quot;&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;.6
$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;build
$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Everything in one&amp;nbsp;command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-arch i386 -arch x86_64&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-Wall -undefined dynamic_lookup -bundle -arch i386 -arch x86_64&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-arch i386 -arch x86_64&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;MACOSX_DEPLOYMENT_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;.6&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;setup.py&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;install
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I hope this works for you as well as it did for&amp;nbsp;me.&lt;/p&gt;
</content><category term="2010"></category><category term="python"></category><category term="scientific"></category><category term="numpy"></category><category term="scipy"></category><category term="os x"></category></entry><entry><title>Collectors 1.0</title><link href="https://stefan.sofa-rockers.org/2010/06/24/collectors-10/" rel="alternate"></link><published>2010-06-24T19:52:59+02:00</published><updated>2010-06-24T19:52:59+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2010-06-24:/2010/06/24/collectors-10/</id><summary type="html">&lt;p&gt;It took me nearly three months to fix five small issues with the documentation.
But now I finally released &lt;em&gt;Collectors v1.0&lt;/em&gt;.&amp;nbsp;:-)&lt;/p&gt;
&lt;p&gt;You can read everything important in the &lt;a href="/2010/04/03/collectors-10-rc1/"&gt;&lt;span class="caps"&gt;RC1 …&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;It took me nearly three months to fix five small issues with the documentation.
But now I finally released &lt;em&gt;Collectors v1.0&lt;/em&gt;.&amp;nbsp;:-)&lt;/p&gt;
&lt;p&gt;You can read everything important in the &lt;a href="/2010/04/03/collectors-10-rc1/"&gt;&lt;span class="caps"&gt;RC1&lt;/span&gt; posting&lt;/a&gt;.&lt;/p&gt;
</content><category term="2010"></category><category term="python"></category><category term="scientific"></category><category term="collectors"></category><category term="simpy"></category></entry><entry><title>Collectors 1.0-rc1</title><link href="https://stefan.sofa-rockers.org/2010/04/03/collectors-10-rc1/" rel="alternate"></link><published>2010-04-03T10:39:15+02:00</published><updated>2010-04-03T10:39:15+02:00</updated><author><name>Stefan Scherfke</name></author><id>tag:stefan.sofa-rockers.org,2010-04-03:/2010/04/03/collectors-10-rc1/</id><summary type="html">&lt;p&gt;&lt;a href="/collectors/"&gt;Collectors&lt;/a&gt; made a huge jump von v0.1 to v1.0 over the last
weeks, since we added lots of changes and consider what we’ve done as stable.
If …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="/collectors/"&gt;Collectors&lt;/a&gt; made a huge jump von v0.1 to v1.0 over the last
weeks, since we added lots of changes and consider what we’ve done as stable.
If we don’t find any bugs, we’ll release the final v1.0 at the end of next
week. The changes&amp;nbsp;are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;]&amp;nbsp;Documentation!&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;]&amp;nbsp;Tests!&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;] Collectors can use different storage backends&amp;nbsp;now&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;] Storage backend for&amp;nbsp;PyTables&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;] Storage backend for &lt;span class="caps"&gt;MS&lt;/span&gt;&amp;nbsp;Excel&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;NEW&lt;/span&gt;] &lt;code&gt;Collector.collect()&lt;/code&gt; as alias to &lt;code&gt;Collector.__call__()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;CHANGE&lt;/span&gt;] &lt;code&gt;Monitor&lt;/code&gt; is now called &lt;code&gt;Collector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;[&lt;span class="caps"&gt;CHANGE&lt;/span&gt;] Shortcut functions moved to &lt;code&gt;shortcuts&lt;/code&gt; package&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can download and install &lt;em&gt;Collectors&lt;/em&gt; via &lt;a href="http://pypi.python.org/pypi/Collectors/"&gt;PyPI&lt;/a&gt;. If you find any bugs or have ideas
for further enhancement, please &lt;a href="http://bitbucket.org/sscherfke/collectors/issues/"&gt;let us know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Documentation can be found &lt;a href="/docs/collectors"&gt;here&lt;/a&gt;.&lt;/p&gt;
</content><category term="2010"></category><category term="python"></category><category term="scientific"></category><category term="collectors"></category><category term="simpy"></category></entry></feed>