<?xml version="1.0" encoding="utf-8"?><feed xml:lang="en" xmlns:atom="http://www.w3.org/2005/Atom"><title>Jeremy Satterfield</title><link rel="alternate" href="https://jsatt.com/blog/"/><link rel="self" href="https://jsatt.com/blog/feeds/atom/"/><id>https://jsatt.com/blog/</id><updated>2026-03-03T11:00:50-06:00</updated><subtitle>Coding, Making and Tulsa Life</subtitle><category term="development"/><entry><title>Resume</title><link href="https://jsatt.com/resume/" rel="alternate"/><published>2026-03-02T14:50:00+00:00</published><updated>2026-03-03T11:00:50-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/resume/</id><summary type="html">&lt;div class="col-md-8 col-md-offset-2" id=""&gt;
&lt;p&gt;Full-stack engineer with over 19 years of experience building web applications and APIs primarily using Python and Django. Emphasis on high-quality, maintainable code and reliable, modern infrastructure. Proven leadership in designing systems architecture, mentoring teammates and delivering reliable software for complex business needs.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="skills"&gt;Skills&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Languages &amp;amp; Frameworks:&lt;/strong&gt; Python, Django, Django REST Framework, Celery, Starlette, JavaScript, React, Angular.js, Stylus, CSS, HTML&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Databases:&lt;/strong&gt; PostgreSQL, MySQL, Redis&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloud &amp;amp; DevOps:&lt;/strong&gt; AWS, GCP, Docker, Kubernetes, ArgoCD, SaltStack, Terraform, Scalr, GitLab CI/CD, GitHub Actions, Spinnaker&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitoring &amp;amp; Tools:&lt;/strong&gt; Datadog, Kafka, Nginx, Graphite/Statsd, StackStorm&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OS &amp;amp; Systems:&lt;/strong&gt; Linux Administration, Serial Communication, Hardware Integration&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="experience"&gt;Experience&lt;/h2&gt;
&lt;h3 id="peloton"&gt;Peloton&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Staff Software Engineer&lt;/strong&gt; (Sep 2022 - Jan 2026)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Senior Software Engineer&lt;/strong&gt; (Nov 2020 - Sep 2022)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technical lead for the B2B team, defining software architecture, infrastructure needs, team development processes and security guidelines.&lt;/li&gt;
&lt;li&gt;Designed and implemented an API and dynamic data processing workflow for Corporate Wellness partners, supporting dynamic, per-partner data formats and workflows to provide employee eligibility data.&lt;/li&gt;
&lt;li&gt;Developed an SSO-based onboarding flow, enabling secure eligibility checks and signup, improving security and user experience.&lt;/li&gt;
&lt;li&gt;Led multiple infrastructure migration projects, including transitioning hosting providers from GCP to AWS, Kubernetes cluster replacement, and moving CI/CD pipelines from Gitlab to Github.&lt;/li&gt;
&lt;li&gt;Mentored other engineering team members and collaborated with team leadership on technical planning and team roadmaps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt; Linux, Python, Django, PostgreSQL, REST, GraphQL, Kafka, Redis, Pandas, Celery, AWS, Kubernetes, ArgoCD, Terraform, Scalr, Datadog&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="peerfit"&gt;Peerfit&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Senior Backend Software Engineer&lt;/strong&gt; (May 2019 - Nov 2020)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built API for external gym partner integrations to communicate member participation.&lt;/li&gt;
&lt;li&gt;Re-architected the core corporate wellness data processing pipelines managing member eligibility and signups.&lt;/li&gt;
&lt;li&gt;Contributed to team development process improvements and system reliability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Linux, Python, Django, PostgreSQL, Django Rest Framework, Starlette, Kubernetes, Google Cloud Platform, Gitlab CI/CD&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="summit-esp-acquired-by-halliburton-in-2017"&gt;Summit ESP (acquired by Halliburton in 2017)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Senior Software Developer&lt;/strong&gt; (Dec 2014 - Apr 2019)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Led architecture and development of internal ticketing and ERP systems.&lt;/li&gt;
&lt;li&gt;Architected migration from monolithic application to specialized microservices.&lt;/li&gt;
&lt;li&gt;Developed PySide/Qt desktop application to provide data entry and syncing with the ERP.&lt;/li&gt;
&lt;li&gt;Managed DevOps infrastructure and transitioning to a container-based deployment pipeline.&lt;/li&gt;
&lt;li&gt;Involved in planning and design of new features with product managers and end users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Linux, Python, Django, MySQL, Javascript, CoffeeScript, Angular.js, Stylus/CSS, SaltStack, Docker, Kubernetes, Spinnaker, Amazon Web Services, Grunt, Gulp, StackStorm, Graphite/Statsd, Hubot&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Projects:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/SummitESP/salt-states"&gt;Infrastructure Salt States&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsatt/python-catalog"&gt;Python Catalog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsatt/django-db-email-backend"&gt;Django DB Email backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsatt/rest-client"&gt;Python REST Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SummitESP/django-auth-ldap"&gt;Django LDAP Auth Backend&lt;/a&gt;/&lt;a href="https://github.com/SummitESP/summit-auth-ldap"&gt;Summit LDAP Backend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="consumeraffairscom"&gt;ConsumerAffairs.com&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Senior Developer&lt;/strong&gt; (Dec 2010 - Dec 2014)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Converted site from static PHP to a dynamic Python/Django architecture.&lt;/li&gt;
&lt;li&gt;Improved site performance and SEO, increasing scalability and site traffic.&lt;/li&gt;
&lt;li&gt;Led development on SaaS product which grew to become the company&amp;rsquo;s primary revenue source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Linux, Python, Django, MySQL, Javascript, CSS, SaltStack, Celery&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Projects:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ConsumerAffairs/django-affect"&gt;Django Affect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ConsumerAffairs/django-urlographer"&gt;Django Urlographer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsatt/django-test-utilities"&gt;Django Test Utilities&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="opentundra"&gt;OpenTundra&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Web Developer&lt;/strong&gt; (Dec 2008 - Dec 2010)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built API, web and reporting interfaces for vehicle telemetry and asset tracking applications.&lt;/li&gt;
&lt;li&gt;Developed kiosk software for fluid dispensing control and reporting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Python, Django, Coldfusion, MySQL, Javascript, CSS, Serial Communication, Hardware Interaction&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="att-mobility"&gt;AT&amp;amp;T Mobility&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Analyst/Developer&lt;/strong&gt; (Mar 2007 - Nov 2008)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built and maintained for several intranet applications, including custom CRMs and reporting interfaces.&lt;/li&gt;
&lt;li&gt;Developed role-based authentication framework internal applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Coldfusion, MS-SQL Server, Javascript, CSS&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="community-involvement"&gt;Community Involvement&lt;/h2&gt;
&lt;h3 id="tulsawebdevs-volunteer-2010---2020"&gt;TulsaWebDevs (Volunteer, 2010 - 2020)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Organized events, civic hackathons, and meetups to grow the local dev community.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Projects:&lt;/strong&gt; &lt;a href="https://github.com/tulsawebdevs/tulsa-road-issues-feed"&gt;TRIF&lt;/a&gt;, &lt;a href="https://github.com/tulsawebdevs/tulsa-transit-google"&gt;Tulsa Transit GTFS&lt;/a&gt;, &lt;a href="https://github.com/tulsawebdevs/django-boundaryservice"&gt;Django Boundary Service&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="code-for-tulsa-delivery-lead-2014---2019"&gt;Code for Tulsa (Delivery Lead, 2014 - 2019)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Coordinated projects and connected volunteers to civic tech initiatives.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="civic-ninjas-developer-2013---2014"&gt;Civic Ninjas (Developer, 2013 - 2014)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Built tools for analyzing health demographics in local communities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Projects:&lt;/strong&gt; &lt;a href="https://github.com/CivicNinjas/SitegeistHealth"&gt;HealthAround.me&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="education"&gt;Education&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tulsa Community College&lt;/strong&gt; - Computer Science (2001 - 2003)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tulsa Technology Center&lt;/strong&gt; - Computer Science (1999 - 2002)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jsatt.com"&gt;Personal Blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsatt"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/jsatterfield"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="col-md-2" id=""&gt;
&lt;ul class="nav" id="sidebar-nav" data-spy="affix"&gt;
&lt;li&gt;&lt;a href="#skills"&gt;Skills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#experience"&gt;Experience&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#community-involvement"&gt;Community Involvement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#education"&gt;Education&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#links"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</summary></entry><entry><title>The Pains of MySQL Perfomance Under Docker</title><link href="https://jsatt.com/blog/mysql-docker-performance/" rel="alternate"/><published>2018-01-15T21:50:21+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/mysql-docker-performance/</id><summary type="html">
&lt;p&gt;Over the course of the last several days I&amp;#39;ve been plagued with a couple pretty massive performance issues running MySQL under Docker(-compose) in our developer environments. Everyone in our office is running either a Mac or some flavor of Linux. In both cases I spent a couple of days search for a solution, running into several roadblocks, and exhausting many Google search terms. Since I had such a difficult time finding the solution, here are the symptoms I was seeing and what finally worked in our case, maybe it&amp;#39;ll save someone else time.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;In both cases we were seeing incredibly long query times in certain areas of the site. The particular queries were those which we would expect to see performance issues exacerbated if they existed, large result sets with several joins and sorted. However, running the same queries against the same data on the same machines against MySQL installed natively on the host still ran these queries in fractions of a second, while the Docker instances took several minutes and spiked the CPU usage. This is an existing app with existing data, but only about 6GB on disk, so nothing big.&lt;/p&gt;
&lt;p&gt;We are using MySQL&amp;#39;s official images on Docker Hub. Using MySQL 5.6 to match what we currently have on our production databases. No customizations to speak of other than setting environment variables for user credentials and mounting named volumes to load and store the data.&lt;/p&gt;
&lt;h2&gt;The Issue with Mac&lt;/h2&gt;
&lt;p&gt;This was the fairly simple one to fix. After past experiences with similar CPU pegging performance issues with MySQL, I suspected this to be something to do with IO being limited somewhere. It took a bit of looking around, but I eventually ran across some forum posts talking about fsync flushing issues. I&amp;#39;ve run across enough similar issues in the past, it&amp;#39;s a wonder my mind doesn&amp;#39;t start there.&lt;/p&gt;
&lt;p&gt;So reading on, I came across &lt;a href="https://gist.github.com/kortina/67ad6e40e40d5199c3507cdad0c9a12c"&gt;this gist&lt;/a&gt; which is a script to turn off fsync within the Hyperkit that Docker is running under Docker for Mac. This did the trick. I started up the docker containers and loaded page with the worst offending queries and it loaded in a matter of seconds.&lt;/p&gt;
&lt;p&gt;Since this was an issue with something as low level as fsync, you&amp;#39;d be right in thinking that there&amp;#39;s an issue with the driver Docker is using to access the disk. Further reading in one of the Github issues I found that they had apparently made changes&amp;nbsp;in 17.11+ to use`raw` format which no longer needed these fsync changes. Docker 17.12 landed in stable a couple days later and appears to have indeed fixed the issue.&lt;/p&gt;
&lt;h2&gt;The Issue with Linux&lt;/h2&gt;
&lt;p&gt;This proved to be a much tricker issue to fix and the solution turned out to be one I tried by chance and not anything I found existing on the web. Like Mac I was seeing CPU spike for complex queries and several minutes to return results. It turns out the Mac issue is wide spread enough that it&amp;#39;s nearly impossible to do a Google search that gives you Linux specific steps to try to narrow it down.&lt;/p&gt;
&lt;p&gt;Here is a short list of many of the steps that &lt;strong&gt;did not work or showed minimal improvements&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;forcing Docker&amp;#39;s storage driver to something other than the default (I ended up on overlay2 because it&amp;#39;s recommended, but again there&amp;#39;s no noticeable difference)&lt;/li&gt;
&lt;li&gt;using a bind (named) volume for storing data files&lt;/li&gt;
&lt;li&gt;using a new mount volume from the host machine&lt;/li&gt;
&lt;li&gt;using a mount volume to the existing data files for my host machines existing MySQL data files, which worked great with a native instance&lt;/li&gt;
&lt;li&gt;upgrading to MySQL 5.7&lt;/li&gt;
&lt;li&gt;providing a custom settings file with tuned settings that worked for the native instance&lt;/li&gt;
&lt;li&gt;running a standalone Docker instance (read: not compose) with minimal parameters provided&lt;/li&gt;
&lt;li&gt;change how my root file system was mounted on boot to include &lt;code&gt;barrier=0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point I was 3 days in and pretty frustrated. I decided to change tactics, and I set out to compare the performance of the Docker vs native instance in a better way. I started out by comparing the &lt;code&gt;EXPLAIN&lt;/code&gt; results for the same query in both environments, and instantly noticed a pretty big difference, the Docker instance wasn&amp;#39;t using a pretty important &lt;code&gt;PRIMARY&lt;/code&gt; index which the native instance is using. I dug a little deeper and no matter how the data was stored (bind v mount) if it was going through the Docker instance it wasn&amp;#39;t using the index. I verified that the index existing in all instances.&lt;/p&gt;
&lt;p&gt;Finally, for no reason that I can particularly pin down, I decided to run MySQL&amp;#39;s optimize script on all the tables. That fixed the issue. I have no idea why this fixed anything, but it has done the trick for all the Linux systems in our office. If you have an explanation I&amp;#39;d be happy to hear about it down in the comments.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Python Catalog Data Structures</title><link href="https://jsatt.com/blog/python-catalog/" rel="alternate"/><published>2017-11-10T15:07:49+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/python-catalog/</id><summary type="html">
&lt;p&gt;I recently realized I have several open-source projects I&amp;#39;m a maintainer or owner of which I&amp;#39;ve never really discussed on this site or on social media. Some of these projects have been pretty specific and tied to the other projects I was working on at the time, but several do have the potential for wider general use. I figured now was a good time to start talking about some of these projects and the problems they are intended to solve.&lt;/p&gt;
&lt;h2&gt;Python Catalogs&lt;/h2&gt;
&lt;p&gt;This is one of the more general use case projects I maintain. And while the examples and original impetus are for choices in Django, Catalogs could be used for just about any Python project that needs complex mappings. It&amp;#39;s a data structure that&amp;#39;s very simple to define and very flexible to use.&lt;/p&gt;
&lt;p&gt;You can install Catalog yourself via Pypi, &lt;code&gt;pip install pycatalog&lt;/code&gt;. As of the latest release it supports Python 2.7 and 3.3+. And you can always submit issues, feature requests and PRs over on &lt;a href="https://github.com/jsatt/python-catalog"&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;The Choice Definition Problem&lt;/h3&gt;
&lt;p&gt;One of the niceties that Django brings to the table for both models and forms, introduced in the some of the earliest tutorials, is allowing you to define choices. It&amp;#39;s a simple pattern that&amp;#39;s not limited to Django, Python or web development, but is weaved through much of Django&amp;#39;s out-of-the-box magic. It makes entry easier for users by providing their options to choose from and easier for your code to validate the input.&lt;/p&gt;
&lt;p&gt;It starts out easy enough: provide the database value and label for each choice item.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class SomeModel(models.Model):
status = models.IntegerField(choices=((1, &amp;#39;Open&amp;#39;), (2, &amp;#39;Closed&amp;#39;)), default=1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple, but right off the bat this example exposes one of the first problems. Anytime you want to actually reference the value, &lt;code&gt;default=1&lt;/code&gt; in this case, you have to reference the value that&amp;#39;s going into the database. Hard coding these values can make it difficult to refactor as your project grows and also make code checking for these values crypt and unclear. Take, for example, the simplest of checks &lt;code&gt;if sm.status == 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A better, more common pattern is to define these values as constants.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class SomeModel(models.Model):
OPEN = 1
CLOSED = 2
STATUS_CHOICES = (
(OPEN, &amp;#39;Open&amp;#39;),
(CLOSED, &amp;#39;Closed&amp;#39;),
)
status = models.IntegerField(choices=STATUS_CHOICES, default=OPEN)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s much cleaner and the code references are clearer too, &lt;code&gt;if sm.status == SomeModel.OPEN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In most cases this is going to be exactly what your project needs and you&amp;#39;re done. Good work.&lt;/p&gt;
&lt;h3&gt;More Complex Cases&lt;/h3&gt;
&lt;p&gt;But what if data from this model syncs with a legacy remote API which has it&amp;#39;s own archaic idea of how these values should be presented? Not too hard, we can add a mapping for that. Well, actually, two mappings because we need to communicate both directions. No problem.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class SomeModel(models.Model):
OPEN = 1
CLOSED = 2
STATUS_CHOICES = (
(OPEN, &amp;#39;Open&amp;#39;),
(CLOSED, &amp;#39;Closed&amp;#39;),
)
API_STATUS_MAP = {
&amp;#39;op&amp;#39;: OPEN,
&amp;#39;cl&amp;#39;: CLOSED,
}
REVERSE_API_STATUS_MAP = {v: k for k, v in API_STATUS_MAP.items()}
status = models.IntegerField(choices=STATUS_CHOICES, default=OPEN)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And sometimes we&amp;#39;ll want to go directly from the API value to the user-readable label. Maybe use a property or method for that? That might obscure the fact that the value is based on the status field, sometimes that&amp;#39;s good, but maybe not this time.&lt;/p&gt;
&lt;p&gt;Also, we want to use a specific template path for a re-used block that changes based on the choices, add another mapping.&lt;/p&gt;
&lt;p&gt;Oh, and maybe a different parser class to abstracts the different parsing details based on the choice, add another mapping.&lt;/p&gt;
&lt;p&gt;This is where things are starting to go haywire. More mappings, more properties and methods, more mess.&lt;/p&gt;
&lt;h3&gt;Enter Python Catalog&lt;/h3&gt;
&lt;p&gt;This is the snowball I was trying to stop when I started working on Catalog a couple years ago. We had a complex project that required a complicated web of mappings and lookup patterns. So I set out to find a data structure that could handle this case with a couple of goals in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple definition signature&lt;/li&gt;
&lt;li&gt;Developers could define their own attributes for the choices, to fit their use case&lt;/li&gt;
&lt;li&gt;Attributes could store values of arbitrary types&lt;/li&gt;
&lt;li&gt;Simple process for mapping items down to an iterable, containing just the attribute values needed at that point&lt;/li&gt;
&lt;li&gt;Easily lookup items by any attribute and access any attribute&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting with the simple signature, Catalog works similar to &lt;a href="https://docs.python.org/3/library/enum.html"&gt;Python 3&amp;#39;s Enum&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class STATUSES(Catalog):
open = 1
closed = 2
STATUSES.open.value # =&amp;gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that doesn&amp;#39;t even handle our simplest case of a database value and a label. We can do better. Let&amp;#39;s define more attributes.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class SomeModel(models.Model):
class STATUSES(Catalog):
_attrs = value, label, api_value
open = 1, &amp;#39;Open&amp;#39;, &amp;#39;op&amp;#39;
closed = 2, &amp;#39;Closed&amp;#39;, &amp;#39;cl&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, then there&amp;#39;s the template paths and parser classes.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class SomeModel(models.Model):
class STATUSES(Catalog):
_attrs = value, label, api_value, template, parser
open = 1, &amp;#39;Open&amp;#39;, &amp;#39;op&amp;#39;, &amp;#39;some_path/open.html&amp;#39;, OpenParser
closed = 2, &amp;#39;Closed&amp;#39;, &amp;#39;cl&amp;#39;, &amp;#39;other_path/closed.html&amp;#39;, ClosedParser&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;#39;s add those choices to the field. There&amp;#39;s an easy method for that.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt; status = models.IntegerField(choices=STATUSES._zip(&amp;#39;value&amp;#39;, &amp;#39;label&amp;#39;), default=STATUSES.open.value)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I want to get the user-readable label, but I&amp;#39;ve got the remote api value, or parser based database value.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;SomeModel.STATUSES(&amp;#39;op&amp;#39;, &amp;#39;api_value&amp;#39;).label # =&amp;gt; &amp;#39;Opened&amp;#39;
SomeModel.STATUSES(1, &amp;#39;value&amp;#39;).parser # =&amp;gt; OpenParser
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So there you have it. It&amp;#39;s by no means perfect, but after using it in production environments for over a year now, we still find it to be much cleaner and easier to use then some of the other patterns out there.&lt;/p&gt;
&lt;p&gt;While the access API is always just as simple, the definitions do get clunkier as you add more attributes to the items. It was never meant to handle dozens of attributes per item and you have bigger problems if that&amp;#39;s your use case, but since the attribute values for each item are really just presented as tuples, you can easily throw in parentheses and wrap across lines to help with readability.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Manage All the Languages Using Python Virtualenv</title><link href="https://jsatt.com/blog/virtualenvs-for-all/" rel="alternate"/><published>2017-10-28T01:17:32+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/virtualenvs-for-all/</id><summary type="html">
&lt;p&gt;A couple of years ago I was working with a couple other languages beside Python and began to get frustrated with their virtualenv equivalents. Not that they were doing anything wrong, just that they didn&amp;#39;t work the way I was used to with virtualenvs. On top of that, they didn&amp;#39;t work well together. I wanted to see if I could get something working that used the workflow I was used to and managed all the languages I was working with. So after a little poking around I found that many of the newer, &amp;quot;modern&amp;quot; languages had ways of running them from custom paths. I did a bit of work and before long I had a couple of these languages installed in virtualenvs and working side-by-side.&lt;/p&gt;
&lt;p&gt;Then, a couple days ago I ran across &lt;a href="https://twitter.com/freakboy3742/status/923316996489408512"&gt;this conversation on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote data-lang="en"&gt;
&lt;p dir="ltr" lang="en"&gt;Most common Python issue I see with students is they&amp;#39;ve installed Python three or four different ways &amp;amp; have all their paths confused. 1/&lt;/p&gt;
&amp;mdash; Jake VanderPlas (@jakevdp) &lt;a href="https://twitter.com/jakevdp/status/922846245848150016?ref_src=twsrc%5Etfw"&gt;October 24, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-lang="en"&gt;
&lt;p dir="ltr" lang="en"&gt;I need to write up how I manage hundreds of projects without this being a problem. It took years to figure it out but it works. &lt;a href="https://t.co/xM3fTs7e6u"&gt;https://t.co/xM3fTs7e6u&lt;/a&gt;&lt;/p&gt;
&amp;mdash; Jeff Triplett ✨ (@webology) &lt;a href="https://twitter.com/webology/status/923068721844948992?ref_src=twsrc%5Etfw"&gt;October 25, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-lang="en"&gt;
&lt;p dir="ltr" lang="en"&gt;A new development I&amp;#39;m really excited about is not just virtualenv, but also putting any non-Python executables in the virtualenvwrapper bin dir so it&amp;#39;s associated with the project too.&lt;/p&gt;
&amp;mdash; Rachel SKellyton 🔮 (@wholemilk) &lt;a href="https://twitter.com/wholemilk/status/923078806239264768?ref_src=twsrc%5Etfw"&gt;October 25, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-lang="en"&gt;
&lt;p dir="ltr" lang="en"&gt;I have flirted with that for non-Python over the years. I liked it but lacked enough Node/Ruby knowledge to pull it off medium term.&lt;/p&gt;
&amp;mdash; Jeff Triplett ✨ (@webology) &lt;a href="https://twitter.com/webology/status/923160645973012480?ref_src=twsrc%5Etfw"&gt;October 25, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;blockquote data-lang="en"&gt;
&lt;p dir="ltr" lang="en"&gt;THIS. It&amp;rsquo;s always intrigued me that there isn&amp;rsquo;t a &amp;ldquo;virtualenv for *all*&amp;rdquo; - one env, virtualising Py, Ruby, C&amp;hellip; whatever.&lt;/p&gt;
&amp;mdash; Russell Keith-Magee (@freakboy3742) &lt;a href="https://twitter.com/freakboy3742/status/923316996489408512?ref_src=twsrc%5Etfw"&gt;October 25, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;And I realized that I had solved this problem for myself a while back and never really mentioned to anyone except a couple coworkers from time to time, and I certainly never shared it on this site or social media. So I&amp;#39;m going to take this opportunity to share how I manage several different languages using virtualenv.&lt;/p&gt;
&lt;p&gt;One last note before we begin. I&amp;#39;m exclusively a Linux user and have been for many years. Most of these commands will work on Macs through the magic of Unix, but I have neither the knowledge or tools to troubleshoot Mac or Windows specific problems. Feel free to share solutions to problems you run into in the comments for others, but I will likely not be able to help too much for those OS&amp;#39;s. If you&amp;#39;re on Windows, please just use the &lt;a href="https://msdn.microsoft.com/en-us/commandline/wsl/about"&gt;Subsytem for Linux&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Python&lt;/h2&gt;
&lt;p&gt;With virtualenv being part of the Python tool chain, you&amp;#39;re clearly going to need Python up and running. On Linux and Mac, your system probably already has it installed and ready to go. If your project works fine on this version or you&amp;#39;re only managing other languages, this version should be fine. If you want to use a specific version, check out&amp;nbsp;&lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt; or &lt;a href="https://www.python.org/downloads/"&gt;downloading the source&lt;/a&gt;, but they each have docs that can walk you through those processes.&lt;/p&gt;
&lt;h3&gt;Installing virtualenv&lt;/h3&gt;
&lt;p&gt;I recommend, and assume for the rest of this post, using virtualenvwrapper and virtualenvs stored at &lt;code&gt;~/.virtualenv&lt;/code&gt;. If you have other preferences, everything here should still work, but you will need to replace my references to &lt;code&gt;~/.virtualenvs&lt;/code&gt; with the path to your environment.&lt;/p&gt;
&lt;p&gt;Assuming you already have &lt;a href="https://pip.pypa.io/en/stable/installing/"&gt;pip installed&lt;/a&gt;, with your Python version, you need to make sure that &lt;code&gt;~/.local/bin&lt;/code&gt; (or &lt;code&gt;/home/&amp;lt;user&amp;gt;/.local/bin&lt;/code&gt; or &lt;code&gt;/Users/&amp;lt;user&amp;gt;/.local/bin&lt;/code&gt;) is in your $PATH (&lt;code&gt;echo $PATH&lt;/code&gt;). If not, add the following to your &lt;code&gt;~/.profile&lt;/code&gt; (or .zprofile, config.fish, etc.)&lt;/p&gt;
&lt;pre class="language-bash"&gt;
&lt;code&gt;PATH=~/.local/bin:$PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you needed to make this change, you should probably reboot your machine now to load that value into all your shell sessions.&lt;/p&gt;
&lt;p&gt;This is going to allow you to use the &lt;code&gt;--user&lt;/code&gt; flag for pip which installs the packages specified at a &amp;quot;global&amp;quot; level just for that user. This is useful for packages like &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;virtualenvwrapper&lt;/code&gt;, &lt;code&gt;ipdb&lt;/code&gt;, etc which can be used to manage Python and your environments. This should NOT be used for packages used within your project.&lt;/p&gt;
&lt;p&gt;So with that set, install virtualenv:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;pip install --user virtualenv virtualenvwrapper&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you want need to edit your &lt;code&gt;~/.profile&lt;/code&gt; (or equivalent) and add the following and restart your shell.&lt;/p&gt;
&lt;pre&gt;
export WORKON_HOME=$HOME/.virtualenvs
source $HOME/.local/bin/virtualenvwrapper.sh
&lt;/pre&gt;
&lt;h3&gt;Create an Virtual Environment&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt; make this easy. If you&amp;#39;re using your OS&amp;#39;s default Python, just use the following.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;mkvirtualenv myenv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to use a different version you&amp;#39;ve installed elsewhere, use the &lt;code&gt;-p&lt;/code&gt; flag to specify it&amp;#39;s path.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;mkvirtualenv myenv -p /path/to/python&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From now on, to activate this environment again just use: &lt;code&gt;workon myenv&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Using virtualenvs&lt;/h3&gt;
&lt;p&gt;Since you&amp;#39;re opting to use virtualenvs for managing other languages, I&amp;#39;m going to assume you&amp;#39;re familiar with the day-to-day process of using them. If you&amp;#39;re not familiar, now is a good time to check out the &lt;a href="https://virtualenv.pypa.io/en/stable/"&gt;virtualenv&lt;/a&gt; and &lt;a href="https://virtualenvwrapper.readthedocs.io/en/latest/"&gt;virtualenvwrapper&lt;/a&gt; docs and Google for more info.&lt;/p&gt;
&lt;h2&gt;NodeJs&lt;/h2&gt;
&lt;p&gt;Node is incredibly easy to setup in virtualenvs, thanks largely to the fact that there is a Python package to handle it for you.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;pip install nodeenv&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This package provides a tool that does everything for you. If you are using node in many projects, you might consider just installing nodeenv with the &lt;code&gt;--user&lt;/code&gt; flag as discussed above.&lt;/p&gt;
&lt;p&gt;Now install node using something like one of the following.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;nodeenv -p # install into active virtualenv
nodeenv .env/ # install into a virtualenv at a specific path&lt;/code&gt;
nodeenv -p -n 8.8.1 # install a specific version of node
&lt;/pre&gt;
&lt;p&gt;That&amp;#39;s it. It&amp;#39;ll download and install the appropriate version, then update the activate script for your environment to provide all the environment variables Node needs to know it.&lt;/p&gt;
&lt;p&gt;Keep in mind that npm has the concept of &amp;quot;global&amp;quot; and &amp;quot;local&amp;quot; modules, in this case &amp;quot;local&amp;quot; will continue to be the current working directory where npm is run, and &amp;quot;global&amp;quot; will be in the &lt;code&gt;lib&lt;/code&gt; directory of the virtualenv you specified.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; commands for manage Node in this environment further.&lt;/p&gt;
&lt;h2&gt;Ruby&lt;/h2&gt;
&lt;p&gt;Much like Node, some kind soul has provided a Python package for managing Ruby&amp;nbsp; versions. This may also be a good candidate for installing with the &lt;code&gt;--user&lt;/code&gt; flag if you&amp;#39;re gonig to use it often.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;pip install rubyenv&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you simply install the Ruby version of choice.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;rubyenv install 2.4.2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since it&amp;#39;s building from source you may need to install some system level packages for the build to succeed, but it should let you know.&lt;/p&gt;
&lt;p&gt;Now use the &lt;code&gt;ruby&lt;/code&gt; and &lt;code&gt;gem&lt;/code&gt; commands to setup and run whatever you need.&lt;/p&gt;
&lt;h3&gt;Go (Golang)&lt;/h3&gt;
&lt;p&gt;Go is a little more work to set up, but not much. There&amp;#39;s not a virtualenv aware tool, so you&amp;#39;ll need to download the &lt;a href="https://golang.org/dl/"&gt;Go Tools binaries&lt;/a&gt;. Be sure to grab the appropriate tar.gz archive, not an installer, for your OS. Once you&amp;#39;ve got the archive, you just need to extract it into your virtualenv.&lt;/p&gt;
&lt;pre&gt;
tar -C $VIRTUAL_ENV -xvf download/go1.9.2.linux-amd64.tar.gz --strip 1&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-C $VIRTUAL_ENV&lt;/code&gt; and &lt;code&gt;--strip 1&lt;/code&gt; are particularly important for the contents of the &lt;code&gt;go&lt;/code&gt; directory inside the archive to be extracted into you virtualenv correctly.&lt;/p&gt;
&lt;p&gt;Next, you need to tell Go where it&amp;#39;s based. The best way to do this is have virtualenv set the &lt;code&gt;GOROOT&lt;/code&gt; environment variable for you when you activate the virtualenv. If you only ever use Go inside a virtualenv, you can add the following to you user&amp;#39;s &lt;code&gt;~/.virtualenv/postactivate&lt;/code&gt;, then you only need to do the extraction step above when you create a new virtualenv. If you only want to use virtualenv Go for this one virtualenv, add the following to &lt;code&gt;~/.virtualenv/&amp;lt;env name&amp;gt;/bin/postactivate&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
GOROOT=&amp;quot;$VIRTUAL_ENV&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Deactivate and reactivate your vritualenv and you&amp;#39;re all set. Manage everything using the &lt;code&gt;go&lt;/code&gt; command as usual.&lt;/p&gt;
&lt;h3&gt;Rust&lt;/h3&gt;
&lt;p&gt;Rust&amp;#39;s primary install tool, rustup, has the ability to install into custom paths. You just need to add a couple environment variables to tell it how, and we&amp;#39;ll let virtualenv activation handle that for us.&lt;/p&gt;
&lt;p&gt;Like Go, if you only use Rust within virtualenvs, and want to save a step in the future, you can add the following to &lt;code&gt;~/.virtualenvs/postactivate&lt;/code&gt;. If you just want to set up this one virtualenv, add it to &lt;code&gt;~/.virtualenvs/&amp;lt;env name&amp;gt;/bin/postactivate&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;CARGO_HOME=$VIRTUAL_ENV
RUSTUP_HOME=$VIRTUAL_ENV&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Now&amp;#39;s a good time to deactivate and reactivate your virtualenv to load those variables. And we&amp;#39;re ready to run the rustup setup script.&lt;/p&gt;
&lt;pre&gt;
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path&lt;/pre&gt;
&lt;p&gt;Done. Now &lt;code&gt;rustc&lt;/code&gt;, &lt;code&gt;cargo&lt;/code&gt; and all the other Rust commands should be good to go.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That&amp;#39;s it. Now you&amp;#39;ve got a pretty solid set of tools for developing different languages. They should all run side-by-side in any mixed and matched set you want to use and safely quarantined from the rest of your system.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m sure there are several other languages that can be managed this way, and if I run across more I&amp;#39;ll try to update this post with details. Otherwise I hope this makes your development processes better.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Securing Your Website with Let&amp;#39;s Encrypt</title><link href="https://jsatt.com/blog/securing-your-website-with-lets-encrypt/" rel="alternate"/><published>2016-07-23T23:04:30+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/securing-your-website-with-lets-encrypt/</id><summary type="html">
&lt;p&gt;This past week I gave a short presentation at the monthly &lt;a href="http://tulsawebdevs.org/"&gt;TulsaWebDevs&lt;/a&gt; meeting about setting up a secure website using &lt;a href="https://letsencrypt.org/"&gt;Let&amp;#39;s Encrypt&lt;/a&gt;. I covered a brief (minimal) overview of how SSL/TLS works and a comparison in the processes of the traditional way to acquire an SSL certificate and acquiring a certificate via the ACME protocol. The slides are now up if you&amp;#39;d like to check them out over on my &lt;a href="/presentations/"&gt;Presentations page&lt;/a&gt;. If you&amp;#39;d like to view my speakers notes simply press `s` while viewing the slideshow in Chrome.&lt;br /&gt;
&amp;nbsp;&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Securing Your Website With Let's Encrypt</title><link href="https://jsatt.com/presentations/securing-your-website-with-lets-encrypt/" rel="alternate"/><published>2016-07-21T05:32:32+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/presentations/securing-your-website-with-lets-encrypt/</id><summary type="html">
&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset="utf-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"&gt;
&lt;title&gt;Securing Your Website With Let's Encrypt&lt;/title&gt;
&lt;link rel="stylesheet" href="/media/presentations/letsencrypt/css/reveal.css"&gt;
&lt;link rel="stylesheet" href="/media/presentations/letsencrypt/css/theme/league.css"&gt;
&lt;!-- Theme used for syntax highlighting of code --&gt;
&lt;link rel="stylesheet" href="/media/presentations/letsencrypt/lib/css/zenburn.css"&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="reveal"&gt;
&lt;div class="slides"&gt;
&lt;section&gt;
&lt;h1&gt;Securing Your Website&lt;/h1&gt;
&lt;h2&gt;With Let's Encrypt&lt;/h2&gt;
&lt;h4&gt;(In Under 5 Minutes)&lt;/h4&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;Jeremy Satterfield&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://jsatt.com"&gt;http://jsatt.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jsatt"&gt;https://github.com/jsatt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/jsatt"&gt;@jsatt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a mailto="jsatt@jsatt.com"&gt;jsatt@jsatt.com&lt;/a&gt;&lt;/p&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Web developer for over 9 years.&lt;/li&gt;
&lt;li&gt;Worked for website measuring traffic in the millions/month&lt;/li&gt;
&lt;li&gt;currently working on internal software in oil industry&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;Primer on Protocols&lt;/h2&gt;
&lt;h3 class="fragment"&gt;HTTP&lt;/h3&gt;
&lt;h3 class="fragment"&gt;SSL/TLS&lt;/h3&gt;
&lt;h3 class="fragment"&gt;HTTPS&lt;/h3&gt;
&lt;h3 class="fragment"&gt;HTTP/2&lt;/h3&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;HTTP - Hypertext Transfer Protocol, is the protocol of web&lt;/li&gt;
&lt;li&gt;SSL/TLS - Secure Sockets Layer and Transport Layer Security
&lt;ul&gt;
&lt;li&gt;TLS is evolution of SSL&lt;/li&gt;
&lt;li&gt;Both usually referred to as SSL&lt;/li&gt;
&lt;li&gt;cryptographic protocols that allow clients and servers to communicate securely&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HTTPS - HTTP over TLS, HTTP over SSL, HTTP Secure&lt;/li&gt;
&lt;li&gt;HTTP/2 - new version of http&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;support by latest nginx and apache, slew of other web servers&lt;/li&gt;
&lt;li&gt;support by every modern browser, over TLS only&lt;/li&gt;
&lt;li&gt;other features out of scope&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;TLS Handshake&lt;/h2&gt;
&lt;p&gt;&lt;img title="http://rebecca.meritz.com/ggm15/#/19" src="/media/presentations/letsencrypt/img/tls_handshake.png" /&gt;&lt;/p&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Short: negotiate tls version, trade some random numbers, encrypt messages to verify, start talking application protocol&lt;/li&gt;
&lt;li&gt;Negotiation Phase
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ClientHello&lt;/code&gt; - client sends supported TLS Version, random number, supported ciphers and compression methods&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServerHello&lt;/code&gt; - server sends chosen TLS Version, random number, chosen ciphers and compression methods (based on client's)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Certificate&lt;/code&gt; - server send public key certificate&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ServerHelloDone&lt;/code&gt; - server indicates handshake done&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ClientKeyExchange&lt;/code&gt; - client sends &lt;code&gt;PreMasterSecret&lt;/code&gt; encrypted using server pub key&lt;/li&gt;
&lt;li&gt;Client and server use random numbers and &lt;code&gt;PreMasterSecret&lt;/code&gt; to calculate common "master secret"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChangeCipherSpec&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;client sends "I only talk encrpted now"&lt;/li&gt;
&lt;li&gt;client send encrypted &lt;code&gt;Finished&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;server decrypts &lt;code&gt;Finished&lt;/code&gt;, if decrypt fails handshake fails and connection torn down&lt;/li&gt;
&lt;li&gt;server sencs "I only talk encrypted now"&lt;/li&gt;
&lt;li&gt;client decrypts &lt;code&gt;Finished&lt;/code&gt;, if decrypt fails handshake fails and connection torn down&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Application Phase - handshake done, application protocol (HTTP) enabled, application messages encrypted using same methods as &lt;code&gt;Finished&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Shutdown - after all messages sent, connection torndown&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;section&gt;
&lt;h2&gt;Obtaining a Certificate&lt;/h2&gt;
&lt;h3&gt;The old way&lt;/h3&gt;
&lt;ol&gt;
&lt;li class="fragment"&gt;Prepare for Validation&lt;/li&gt;
&lt;li class="fragment"&gt;Generate CSR&lt;/li&gt;
&lt;li class="fragment"&gt;Order Certificate &lt;span class="fragment"&gt;$$$&lt;/span&gt;&lt;/li&gt;
&lt;li class="fragment"&gt;Have domain validated&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Prepare
&lt;ul&gt;
&lt;li&gt;Expose WHOIS info for email confirmation&lt;/li&gt;
&lt;li&gt;Setting server for each domain for HTTP confirmation&lt;/li&gt;
&lt;li&gt;Adding CNAME for DNS confirmation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generate Certificate Signing Request
&lt;ul&gt;
&lt;li&gt;Encrypted details of certificate being requested&lt;/li&gt;
&lt;li&gt;Contains Common Name (domain), organization, location, contact email, public key used in certifcate&lt;/li&gt;
&lt;li&gt;Used to generate SSL certificate&lt;/li&gt;
&lt;li&gt;Also generates private key, hold on to it. Cert useless without it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Order
&lt;ul&gt;
&lt;li&gt;register, fill out contact, submit CSR, Pay $$&lt;/li&gt;
&lt;li&gt;Single domain: $17-250/yr&lt;/li&gt;
&lt;li&gt;Multiple: $99-249/yr + per each additional&lt;/li&gt;
&lt;li&gt;Wildcard: $150-850/yr&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;wait for validate, usually "within minutes", reevaluate life&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section data-background="http://i.giphy.com/l2R0corOGwFTlKZjO.gif"&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;Obtaining a Certificate&lt;/h2&gt;
&lt;h3&gt;The old way&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Prepare for Validation&lt;/li&gt;
&lt;li&gt;Generate CSR&lt;/li&gt;
&lt;li&gt;Order Certificate $$$&lt;/li&gt;
&lt;li&gt;Have domain validated&lt;/li&gt;
&lt;li&gt;Receive and Install Certificate&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Receive and install ticket&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;Let's Encrypt&lt;/h2&gt;
&lt;section&gt;
&lt;img src="/media/presentations/letsencrypt/img/letsencrypt.png" /&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Non-profit ISRG Internet Secutiry Research Group&lt;/li&gt;
&lt;li&gt;Sponsors - EFF, Mozilla Foundation, Akamai, Cisco&lt;/li&gt;
&lt;li&gt;other partners - IdenTrust, Linux Foundation, Chrome, Stanford Law School, many others.&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;p class="fragment"&gt;Free&lt;/p&gt;
&lt;p class="fragment"&gt;Automatic&lt;/p&gt;
&lt;p class="fragment"&gt;Secure&lt;/p&gt;
&lt;p class="fragment"&gt;Transparent&lt;/p&gt;
&lt;p class="fragment"&gt;Open&lt;/p&gt;
&lt;p class="fragment"&gt;Cooperative&lt;/p&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;free - to any domain owner&lt;/li&gt;
&lt;li&gt;Auto - uses software on server to verfiy, obtain, configure and renew certs&lt;/li&gt;
&lt;li&gt;Secure - promotes security best practices, help site ops to secure servers&lt;/li&gt;
&lt;li&gt;Transparent - all issued and revoked certs are public record&lt;/li&gt;
&lt;li&gt;Open - automatic issuance and renewal protocol is open standard, ACME&lt;/li&gt;
&lt;li&gt;Coop - joint effort from many orgs to benefit community, doesn't rely on one org&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;p class="fragment"&gt;Short Validity Time&lt;/p&gt;
&lt;p class="fragment"&gt;No Wildcard Certificates&lt;/p&gt;
&lt;p class="fragment"&gt;No Extended Validation or&lt;br&gt; Organization Validation&lt;/p&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;short - expire in 90 days&lt;/li&gt;
&lt;li&gt;no wildcard - not big deal since certs are free, up to 100 domains on single cert via SAN&lt;/li&gt;
&lt;li&gt;no organization or extended validation - EV shows green "secure" bar&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;ACME&lt;/h2&gt;
&lt;img src="/media/presentations/letsencrypt/img/acme.jpg" /&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;Automated Certificate Management Environment&lt;/li&gt;
&lt;li&gt;Protocol for CAs to interact with other CAs and users&lt;/li&gt;
&lt;li&gt;Allows Automated deployment&lt;/li&gt;
&lt;li&gt;JSON over HTTPS&lt;/li&gt;
&lt;li&gt;Standard currently in draft stage&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;section&gt;
&lt;h2&gt;Obtaining a Certificate&lt;/h2&gt;
&lt;h3&gt;In the modern world&lt;/h3&gt;
&lt;h4&gt;Certbot&lt;/h4&gt;
&lt;pre&gt;&lt;code class="hlbash" data-trim contenteditable&gt;
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
./certbot-auto certonly
&lt;/code&gt;&lt;/pre&gt;
&lt;aside class="notes"&gt;
&lt;ul&gt;
&lt;li&gt;stop nginx&lt;/li&gt;
&lt;li&gt;certonly for nginx&lt;/li&gt;
&lt;li&gt;all domains should be pointing to this IP&lt;/li&gt;
&lt;li&gt;start nginx&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section data-background="http://i.giphy.com/90F8aUepslB84.gif"&gt;&lt;/section&gt;
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;Installing Your Certificate&lt;/h2&gt;
https://mozilla.github.io/server-side-tls/ssl-config-generator/
&lt;ul&gt;
&lt;li&gt;nginx&lt;/li&gt;
&lt;li&gt;Modern&lt;/li&gt;
&lt;li&gt;nignx 1.4.6&lt;/li&gt;
&lt;li&gt;openssl 1.0.1f&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="notes"&gt;
chained cert
&lt;/section&gt;
&lt;section&gt;
&lt;h2&gt;More&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security"&gt;Wikipedia - Transport Layer Security&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
ACME -
&lt;a href="https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment"&gt;Wikipedia&lt;/a&gt;
&lt;a href="https://github.com/ietf-wg-acme/acme"&gt;GitHub&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a href="http://rebecca.meritz.com/ggm15/"&gt;http://rebecca.meritz.com/ggm15/&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;script src="/media/presentations/letsencrypt/lib/js/head.min.js"&gt;&lt;/script&gt;
&lt;script src="/media/presentations/letsencrypt/js/reveal.js"&gt;&lt;/script&gt;
&lt;script&gt;
// More info https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
history: true,
// More info https://github.com/hakimel/reveal.js#dependencies
dependencies: [
{ src: '/media/presentations/letsencrypt/plugin/notes/notes.js', async: true },
{ src: '/media/presentations/letsencrypt/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
]
});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</summary></entry><entry><title>Unit Testing Recursion in Python</title><link href="https://jsatt.com/blog/unit-testing-recursion-in-python/" rel="alternate"/><published>2014-12-11T17:05:39+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/unit-testing-recursion-in-python/</id><summary type="html">
&lt;p&gt;Today I finally figured out the solution to a problem I&amp;#39;ve been trying to solve for a while. It&amp;#39;s kind of hacky and maybe a bad idea, but now I know it&amp;#39;s possible. The problem has always been that I&amp;#39;d like to test that a function recurses, but not needing it to actually have the recursion execute within to test. Just a unit test to assert that recursion is happening. After a little thought about how Python stores references I came up with this.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;class ExampleTest(TestCase):
def setUp(self):
self.task = Task()
# ...
# other test stuff
# ...
def test_recursion(self):
original_func = self.task.run
self.mock.StubOutWithMock(Task, &amp;#39;run&amp;#39;)
Task.run(id=44)
self.mock.ReplayAll()
original_func(id=22)
self.mock.VerifyAll()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because variables in Python are just references to objects in memory, if you create a new reference before stubbing out the primary reference, pointing it to a mock object, you can still access it via this new reference. You&amp;#39;re now free to check that recusion is happening, without the need for it to actually recurse in the test.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Keeping MRO In Mind When Mocking Inherited Methods</title><link href="https://jsatt.com/blog/keeping-mro-in-mind-when-mocking-inherited-methods/" rel="alternate"/><published>2014-10-16T19:33:11+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/keeping-mro-in-mind-when-mocking-inherited-methods/</id><summary type="html">
&lt;p&gt;So I just spent a couple of hours banging my head on a ridiculous PyMox mocking issue and though I&amp;#39;d share. Here is an example of the existing code.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;# views.py
class MyBaseView(BaseView):
def get_stuff(self):
# this is doing things
class SpecificView(MyBaseView):
example = True
def send_stuff(self):
stuff = self.get_stuff()
# do other things
# tests.py
class SpecificViewTest(TestCase):
def setUp(self):
self.view = SpecificView()
self.mox = mox.Mox()
def tearDown(self):
self.mox.UnsetStubs()
def test_send_stuff(self):
self.mox.StubOutWithMock(MyBaseView, &amp;#39;get_stuff&amp;#39;)
MyBaseView.get_stuff().AndReturn([&amp;#39;list&amp;#39;, &amp;#39;of&amp;#39;, &amp;#39;things&amp;#39;])
self.mox.ReplayAll()
self.view.send_stuff()
self.mox.VerifyAll()
# do assertions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was all fine and working for months, until I added the following.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;# views.py
class ExtraSpecificView(SpecificView):
def other_stuff(self):
self.get_stuff()
# do more things again
# test.py
class ExtraSpecificViewTest(TestCase):
def setUp(self):
self.view = ExtraSpecificView()
self.mox = mox.Mox()
def tearDown(self):
self.mox.UnsetStubs()
def test_other_stuff(self):
self.mox.StubOutWithMox(SpecifcView, &amp;#39;get_stuff&amp;#39;)
SpecificView.get_stuff().AndReturn([&amp;#39;more&amp;#39;, &amp;#39;things&amp;#39;])
self.mox.ReplayAll()
self.view.other_stuff()
self.mox.VerifyAll()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So what started happening at this point is &lt;code&gt;SpecificViewTest.test_send_stuff&lt;/code&gt; began failing with this.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;ExpectedMethodCallsError: Verify: Expected methods never called:
0. get_stuff.__call__() -&amp;gt; [&amp;#39;list&amp;#39;, &amp;#39;of&amp;#39;, &amp;#39;things&amp;#39;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would only fail when &lt;code&gt;ExtraSpecificViewTest&lt;/code&gt; ran before &lt;code&gt;SpecificViewTest&lt;/code&gt; which, with nose, it does by default. So I knew it had to have something to do with the mocking in &lt;code&gt;ExtraSpecificViewTest.test_other_stuff&lt;/code&gt;. After more playing and banging my head I came up with this working theory which seems correct the more I think on it.&lt;/p&gt;
&lt;p&gt;When you mock out a method on a class, the mocking library stores the original method and replaces it with the mock function. When you tell the library to stop mocking the method, &lt;code&gt;mox.UnsetStubs&lt;/code&gt; in this case, it grabs that original method it stored before and assigns it back to the appropriate attribute on the class, restoring it&amp;#39;s original functionality.&lt;/p&gt;
&lt;p&gt;However, in my class I was mocking a method that &lt;code&gt;SpecificView&lt;/code&gt; inherited from &lt;code&gt;MyBaseView&lt;/code&gt;. When the library grabs the original method for storing Python sees that SpecificView doesn&amp;#39;t have a &lt;code&gt;get_stuff&lt;/code&gt; method of it&amp;#39;s own, so it moves up the &lt;abbr title="Method Resolution Order"&gt;MRO&lt;/abbr&gt; and grabs &lt;code&gt;MyBaseView.get_stuff&lt;/code&gt; and the library stores that and assigns a mock function to the &lt;code&gt;get_stuff&lt;/code&gt; attribute on the &lt;strong&gt;class&lt;/strong&gt; of &lt;code&gt;SpecificView&lt;/code&gt;. Then, when the library goes to un-stub the method, it grabs the stored &lt;code&gt;get_stuff&lt;/code&gt; Python gave it from &lt;code&gt;MyBaseView&lt;/code&gt; and assigns it also to the &lt;code&gt;get_stuff&lt;/code&gt; attribute of the &lt;strong&gt;class&lt;/strong&gt; of &lt;code&gt;SpecificView&lt;/code&gt;. Finally, when &lt;code&gt;SpecificViewTest.test_set_stuff&lt;/code&gt; runs, &lt;code&gt;send_stuff&lt;/code&gt; calls &lt;code&gt;SpecificView&lt;/code&gt;&amp;#39;s &lt;code&gt;get_stuff&lt;/code&gt;, instead of moving up the MRO as to used to, Python now sees that the &lt;code&gt;SpecificView&lt;/code&gt; class has it&amp;#39;s own &lt;code&gt;get_stuff&lt;/code&gt; method (which doesn&amp;#39;t have a super call), so it doesn&amp;#39;t move up the MRO, therefore &lt;code&gt;MyBaseView&lt;/code&gt; is never called as expected and raising a failure when verified.&lt;/p&gt;
&lt;p&gt;So this became the fix.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;#tests.py
class ExtraSpecificViewTest(TestCase):
...
def test_other_stuff(self):
self.mox.StubOutWithMox(MyBaseView, &amp;#39;get_stuff&amp;#39;)
MyBaseView.get_stuff().AndReturn([&amp;#39;more&amp;#39;, &amp;#39;things&amp;#39;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&amp;#39;s a convoluted bug that may or may not be specific to PyMox or other mocking libraries, but I&amp;#39;m not sure how they should be properly handling something like this. So I guess the moral of the story is to mock methods from the class which the were originally defined, not just the class you inherited from.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Mocking a property in Python</title><link href="https://jsatt.com/blog/mocking-a-property-in-python/" rel="alternate"/><published>2014-08-08T21:10:45+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/mocking-a-property-in-python/</id><summary type="html">
&lt;p&gt;Anytime I see someone turning an instance method into a property on a Python object, I always have to step back and rethink whether it&amp;#39;s really the right thing to do. While properties certain have valid use cases, I often see them overused and misused. This can result in code that is harder to refactor should you decide you actually do want to accept arguments as well as less straight forward to separate in unit tests.&lt;/p&gt;
&lt;p&gt;The way I found to deal with the latter case is pretty simple but unituitive at first. Simply stub out the property on the class, then assign the property value on the instance.&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code&gt;# Use __class__ if you don&amp;#39;t have the class imported
self.mox.StubOutWithMock(self.book.__class__, &amp;#39;reviews&amp;#39;)
self.book.editors = [&amp;#39;editor 1&amp;#39;, &amp;#39;editor 2&amp;#39;]
# replace with a MockAnything to go deeper
self.mock.StubOutWithMock(Company, &amp;#39;employees&amp;#39;)
Company.employees = self.mock.CreateMockAnything()
company.employees.are_executives().AndReturn([&amp;#39;CEO&amp;#39;, &amp;#39;COO&amp;#39;, &amp;#39;CIO&amp;#39;])
campaign3.reviews.are_developers().AndReturn([&amp;#39;Jim&amp;#39;, &amp;#39;John&amp;#39;, &amp;#39;Joey&amp;#39;])&lt;/code&gt;&lt;/pre&gt;</summary><category term="development"/></entry><entry><title>Class-based Celery Tasks</title><link href="https://jsatt.com/blog/class-based-celery-tasks/" rel="alternate"/><published>2014-06-05T23:25:06+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/class-based-celery-tasks/</id><summary type="html">
&lt;p&gt;Update 2017-11-02: Celery 4 now &lt;a href="http://docs.celeryproject.org/en/latest/whatsnew-4.0.html#the-task-base-class-no-longer-automatically-register-tasks"&gt;adivises against inheriting from Task&lt;/a&gt; unless you are extending common functionality. However you can still get similar functionality by creating a new class and calling is from inside a decorated function task.&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;class MyTask(object):
def run(self, source):
do_stuff()
...
@app.task
def my_task(source):
MyTask().run(source)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Original Post:&lt;/p&gt;
&lt;p&gt;Recently, I&amp;#39;ve had to write several complicated Celery tasks. Unfortunately, when doing a complicated process standard task functions can become unwieldy to write, read and unit test. After looking into how Celery tasks actually work, I was able to find a more manageable way of writing these complex tasks.&lt;/p&gt;
&lt;p&gt;It turns out the &lt;code&gt;task&lt;/code&gt; decorator that you&amp;#39;re used to using is just an object factory for &lt;code&gt;Task&lt;/code&gt; objects that turns the decorated function into the &lt;code&gt;run&lt;/code&gt; method of on the &lt;code&gt;Task&lt;/code&gt; instance it creates. Skipping the decorator and extending the &lt;code&gt;Task&lt;/code&gt; class directly makes things a little more flexible.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;from celery import Task
class MyTask(Task):
ignore_result = True
def run(self, source, *args, **kwargs):
self.source = source
data = self.collect_data()
self.generate_file(data)
def generate_file(self, data):
# do your file generation here
...
def collect_data(self):
# do your data collection here
...
return data&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;In this case &lt;code&gt;run&lt;/code&gt; is the equivalent of the function task you&amp;#39;re used to, but thanks to OOP you&amp;#39;re free to break some of the more complex code into logic blocks, &lt;code&gt;collect_data&lt;/code&gt; and &lt;code&gt;generate_file&lt;/code&gt;, and access to instance attribute, &lt;code&gt;source&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To call the task your just need to instantiate the it and call the desired method to trigger it.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;task = MyTask()
# Run on Celery worker now
task.delay(123)
# Run on Celery worker at a sepcfic time
task.apply_async(args=[123], eta=datetime(2015, 6, 5, 12, 30, 22))
# Run task directly, No celery worker
task.run(123)
task(123)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing also now becomes easier as well since you can test each unit on it&amp;#39;s own.&lt;/p&gt;
&lt;p&gt;For most cases, your standard function-based classes are probably going to do the job. But for those extra complex cases, class-based might make things easier work with.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Mocking Python&amp;#39;s built-in open function</title><link href="https://jsatt.com/blog/mocking-pythons-built-in-open-function/" rel="alternate"/><published>2014-04-11T17:25:38+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/mocking-pythons-built-in-open-function/</id><summary type="html">
&lt;p&gt;A while back I was working on my &lt;a href="https://github.com/jsatt/django-podcast-client"&gt;podcast client&lt;/a&gt; and ran into an issue downloading large files. The root problem was that all of Django&amp;#39;s &lt;code&gt;FileField&lt;/code&gt; backends store the file (or file-like object) to memory then saves it to disk, and the cubieboard system I was using had limitied memry resources, resulting in &amp;quot;out of memory&amp;quot; errors. After much searching and hacking I finally settled on just storing the file to disk myself using &lt;a href="http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow"&gt;requests streaming argument&lt;/a&gt;. This allowed me to download the file in chunks and save directly to disk and then tell the Django field where I placed it, as you can see &lt;a href="https://github.com/jsatt/django-podcast-client/blob/master/podcast_client/models.py#L131-L148"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then I went to update tests. This is when a new problem presented itself. I wanted to test that proper calls were being made to store the file to disk, but I didn&amp;#39;t want to store a file to disk during tests. Mocking never worked the way you&amp;#39;d expect for the built-in open. I placed it on the back burner for a while until I ran into a similar issue an another project. When I finally found the solution, it was incredibly simple and incredibly nonobvious.&lt;/p&gt;
&lt;p&gt;Pythons has a bunch of built-in functions that are just preloaded, such as &lt;code&gt;open&lt;/code&gt; for opening files, &lt;code&gt;print&lt;/code&gt; for outputting to the console, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, etc. for casting or creating an object, among others. Most of these just do what they do, with no affect on the overall environment; therefore, when testing I just let them do what they do and mocking them is probably a bad idea anyway. Except for &lt;code&gt;open&lt;/code&gt;, it has real world affects on the environment in that it accesses, and allows writing of, files on the disk.&lt;/p&gt;
&lt;p&gt;Finally, I stumbled across the &lt;code&gt;__builtin__&lt;/code&gt; module. This is the magic module where all those functions actually reside, and if you can access where a function resides you can mock it out. The following should work on most Python mocking frameworks, but this is how to use PyMox to do it.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;class Client(APIClient):
...
def open_file(self):
file = open(self.path, &amp;#39;r&amp;#39;)
contents = file.read()
file.close()
return self.prepare_file(contents)
...
# test.py
import __builtin__
from StringIO import StringIO
class ClientTest(TestCase):
...
def test_open_file(self):
self.mock.StubOutWithMock(__builtin__, &amp;#39;open&amp;#39;)
mock_content = StringIO(&amp;#39;test&amp;#39;)
self.mock.StubOutWithMock(mock_content, &amp;#39;close&amp;#39;)
open(&amp;#39;/tmp/mypath.txt&amp;#39;, &amp;#39;r&amp;#39;).AndReturn(mock_content)
mock_content.close()
self.mock.ReplayAll()
file = self.client.open_file()
self.mock.VerifyAll()
...&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;In this case, returning an instance of StringIO results in a file-like object allowing me to set the contents of the &amp;quot;file&amp;quot; being accessed directly in the test. No fixtures or creating files on the disk. This pattern works for both reads and writes.&lt;/p&gt;
&lt;p&gt;But the code I&amp;#39;m testing here doesn&amp;#39;t do all the error handling to make sure the file is closed even if there&amp;#39;s an error during the read. Frankly, they made it possible to use &lt;code&gt;open&lt;/code&gt; as a context manager so that you don&amp;#39;t have to worry about it. So combining this new knowledge with my previous post about &lt;a href="/blog/mocking-context-managers-in-python/"&gt;mocking context managers&lt;/a&gt;, we can easily write cleaner code that&amp;#39;s still testable.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;class Client(APIClient):
...
def write_file(self):
with open(self.path, &amp;#39;wb&amp;#39;) as f:
f.write(self.content)
...
# test.py
class ClientTest(TestCase):
...
def test_write_file(self):
self.mock.StubOutWithMock(__builtin__, &amp;#39;open&amp;#39;)
mock_file1 = self.mock.CreateMockAnything()
open(&amp;#39;/tmp1/mypath.txt&amp;#39;, &amp;#39;wb&amp;#39;).AndReturn(mock_file1)
mock_file1.__enter__().AndReturn(mock_file1)
mock_file1.write(&amp;#39;this is my content&amp;#39;)
mock_file1.__exit__(None, None, None)
self.mock.ReplayAll()
self.client.write_file()
self.mock.VerifyAll()
...&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Since StringIO can&amp;#39;t be used as a context manager, this pattern doesn&amp;#39;t allow for the use of StringIO. This means you have to mock out the reads, writes and any other methods you would call on a file-like object, but I think that&amp;#39;s a small price for being able to write cleaner, testable code.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Abusing Django Rest Framework Part 4: Object-level field exclusion</title><link href="https://jsatt.com/blog/abusing-django-rest-framework-part-4-object-level-field-exclusion/" rel="alternate"/><published>2014-03-27T16:55:11+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/abusing-django-rest-framework-part-4-object-level-field-exclusion/</id><summary type="html">
&lt;p&gt;Similar to the object-level readonly field from my &lt;a href="/blog/abusing-django-rest-framework-part-3-object-level-read-only-fields/"&gt;previous post&lt;/a&gt;, there are some cases where we want to exclude certain fields based on what object the user is trying to access. You could overwrite the views &lt;code&gt;get_serializer&lt;/code&gt; method to use a different serializer based on their access, but if nesting serializers is a possiblility this get messy, somewhere in the neighborhood of &lt;a href="http://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/" target="_blank"&gt;O(n&lt;sup&gt;2&lt;/sup&gt;)&lt;/a&gt;. Another option is to modify a serializers &lt;code&gt;to_native&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;class SampleSerializer(serializers.ModelSerializer):
...
def to_native(self, obj):
restricted = not obj.review_set.filter(
client__plan__account_details=True).exists()
if restricted:
self.fields.pop(&amp;#39;billing_address&amp;#39;, None)
return super(SampleSerializer, self).to_native(obj)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;to_native&lt;/code&gt; method is responsible for turning the Python object into a simple, serializable value. This means it has to be called for every object that it being retrieved, so it will always be able to affect your output, even when nested.&lt;/p&gt;
&lt;p&gt;One drawback is that it only works in conjunction with retrieve views. If the field is defined in the serializer at any point as writeable, the users can still update that field, even if they can&amp;#39;t see it. This can be a pretty big security risk when used with update views.&lt;/p&gt;
&lt;h2&gt;Another Approach&lt;/h2&gt;
&lt;p&gt;Having implemented this method several months ago, based on a spec that was several months old at the time, I would likely approach the root issue from a different angle if it came up again. Maybe creating updating the spec to have a consistant api response and return blank values for the unauthorized fields would be a better approach. &lt;code&gt;SerializerMthodField&lt;/code&gt; has the potential to make this approach much cleaner.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Abusing Django Rest Framework Part 3: Object-level read-only fields</title><link href="https://jsatt.com/blog/abusing-django-rest-framework-part-3-object-level-read-only-fields/" rel="alternate"/><published>2014-03-26T16:25:04+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/abusing-django-rest-framework-part-3-object-level-read-only-fields/</id><summary type="html">
&lt;p&gt;DRF has tools to control access in a few ways. Serializers make it easy to select what fields can be accessed and whether or not they are read-only. Permissions are great for restricting access to objects at all or even making certain objects read-only. But there are also cases where you might only want to allow access to a field on a specific object but leave that field restricted on other objects, or vice-versa.&lt;/p&gt;
&lt;p&gt;In our case, we had an endpoint for modifying users, which has a field to dictate whether the user is an account admin. Our view already uses permissions to verify that the request user is allow to modify these items at all, but we wanted to make sure the user couldn&amp;#39;t accidentally remove their own admin access but still be able to change other fields on this endpoint.&lt;/p&gt;
&lt;p&gt;Whenever a &lt;code&gt;Serializer&lt;/code&gt; is instantiated, the &lt;code&gt;get_fields&lt;/code&gt; method is called to look at the attributes and decide what fields to include and instantiate them. As part of the serialzer instantiation the view also passes the request, view and &lt;code&gt;format_kwarg&lt;/code&gt; as context to the serializer which is attached to the instance. This means we can use the request or view, or objects attached to them in to change attributes of the fields, in our case making one of them readonly.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class SampleSerializer(serializers.ModelSerializer):
...
def get_fields(self, *args, **kwargs):
fields = super(SampleSerializer, self).get_fields(*args, **kwargs)
request = self.context.get(&amp;#39;request&amp;#39;, None)
view = self.context.get(&amp;#39;view&amp;#39;, None)
if (request and view and getattr(view, &amp;#39;object&amp;#39;, None) and
request.user == view.object.user):
fields[&amp;#39;is_admin&amp;#39;].read_only = True
return fields&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one issue with this method is that you can not nest this serializer and have this change work. Whenever a Serializer is nested on to another, the parent gets one instance of this child at the time that the server process starts, which gets reused for all future instances of said parent. Due to lots of complicated handling on DRF&amp;#39;s part, that I&amp;#39;ll leave to you to read up on, everything just works the way you expect most of the time. However, we are trying to access the request and view objects that clearly don&amp;#39;t exist at the time the server process starts.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Using Tox with Travis CI to Test Django Apps</title><link href="https://jsatt.com/blog/using-tox-with-travis-ci-to-test-django-apps/" rel="alternate"/><published>2014-03-09T01:18:20+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/using-tox-with-travis-ci-to-test-django-apps/</id><summary type="html">
&lt;p&gt;Being a fan of good testing, I&amp;#39;m always trying to find ways to improve testing on various projects. &lt;a href="https://travis-ci.org/"&gt;Travis CI&lt;/a&gt; and &lt;a href="https://coveralls.io/"&gt;Coveralls&lt;/a&gt; are really nice ways to set up continuous integration for your open-source projects. A couple months ago I finally started hearing grumblings about &lt;a href="http://testrun.org/tox/latest/index.html"&gt;tox&lt;/a&gt; and how everyone was it using for their Python test automation. Every time I&amp;#39;d try to wrap my head around it, something always eluded me, so this week I finally decided to dive in head first and see if I could get to the bottom of it and how it could improve my current integration setup.&lt;/p&gt;
&lt;h2&gt;Headless Django Environments&lt;/h2&gt;
&lt;p&gt;Most of my open-source Python projects have been developed from the beginning to be installable modules for use with tools like pip. With Django apps, this tends to be a little more difficult as Django development environments generally assumes you have a project that contains a lot boilerplate (manage.py, project/settings.py, project/urls.py), but some of this stuff is just clutter in an installable package. After trying several patterns, I found the one used by &lt;a href="http://jamessocol.com/"&gt;James Socol&lt;/a&gt; on several projects, using &lt;a href="http://docs.fabfile.org/en/1.8/"&gt;fabric&lt;/a&gt; to wrap my development enviroment and easily provide some of the common commands we use manage.py for, but without all the clutter of a full Django project. I&amp;#39;ve also created a repository that&amp;#39;s intended to be used as a Django &lt;a href="https://docs.djangoproject.com/en/dev/ref/django-admin/#startproject-projectname-destination"&gt;project template&lt;/a&gt;, to help with setting out a project like this. It even includes some of the setup I&amp;#39;m about to discuss. You can get that template &lt;a href="https://github.com/jsatt/django-installable-plugin-template"&gt;over at Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Preparing tox&lt;/h2&gt;
&lt;p&gt;Start by installing tox with a simple &lt;code&gt;pip install tox&lt;/code&gt;. Then create the tox.ini file in the base of your project. The first section you care about is configuring tox itself.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-none"&gt;
[tox]
envlist = django15, django16&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;envlist&lt;/code&gt; directive tells tox which environments to run by default when calling it from the CLI. Tox comes with a few built-in environments (py26, py27, py31, py33, etc.) for testing different Python versions, but in my case I care about testing different Django versions, so I&amp;#39;ll be building my own environments.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-none"&gt;[testenv]
commands = django-admin.py test
setenv =
DJANGO_SETTINGS_MODULE=test_app.settings
PYTHONPATH={toxinidir}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;testenv&lt;/code&gt; is the set of default environment settings that all named environments will inherit from. Here I&amp;#39;m telling it that the default command I&amp;#39;m going to use is &lt;code&gt;django-admin.py test&lt;/code&gt; to run tests. And setup environment variables that are usually handled by &lt;code&gt;manage.py&lt;/code&gt;. Tox also allows for substituting variables, in this case &lt;code&gt;{toxinidir}&lt;/code&gt; is simply the path of the directory which contains the &lt;code&gt;tox.ini&lt;/code&gt; file. It also allows for doing substitutions based on lookups within the ini file. By defining a custom section, we can define settings that can be reused and extended within our environments.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-none"&gt;[base]
deps =
mox
nose
django-nose&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I&amp;#39;m defining some dependencies that I&amp;#39;m going to add to in each environment. In my case, I prefer Nose and Mox to their standard library counterparts.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-none"&gt;[testenv:django15]
deps =
django&amp;gt;=1.5, &amp;lt;1.6
{[base]deps}
[testenv:django16]
deps =
django&amp;gt;=1.6, &amp;lt;1.7
{[base]deps}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates two new test environments, named django15 and django16, and adds the dependency to install the proper version of Django, 1.5 and 1.6 respectively. It also uses the section lookup to append the deps from the base section we defined above. Now, when you return to the CLI and run tox, you should see it setup virtualenvs for each environment and run your tests under them. That&amp;#39;s all it take to get it running. Anyone can now clone your repository and simply run tox to confirm that tests are passing. This is particularly useful if they intend to contribute back to the project.&lt;/p&gt;
&lt;h3&gt;Adding Coverage&lt;/h3&gt;
&lt;p&gt;Now, as I mentioned before, I want to also use Coveralls to show coverage stats as well. To do that I just need to define another environment.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-none"&gt;[testenv:coverage]
commands =
coverage run --branch --omit={envdir}/*,test_app/*.py,*/migrations/*.py {envbindir}/django-admin.py test
coveralls
deps =
coverage
coveralls
{[testenv:django16]deps}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time I&amp;#39;m running &lt;code&gt;coverage run&lt;/code&gt; with branch coverage, ignoring migrations and my test app and running the &lt;code&gt;django-admin.py&lt;/code&gt; in the enviroments bin directory, followed by the coveralls reporting script. We also have a couple of additional dependencies to add to the django16 deps.&lt;/p&gt;
&lt;h2&gt;Running Tox on Travis&lt;/h2&gt;
&lt;p&gt;With tox configured to handle the details, Travis just needs to know about the different environments. Here&amp;#39;s what the &lt;code&gt;.travis.yaml&lt;/code&gt; looks like.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-yaml"&gt;language: python
install:
- pip install tox
script:
- tox
env:
- TOXENV=django15
- TOXENV=django16
- TOXENV=coverage&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Travis to install tox and to run the &lt;code&gt;tox&lt;/code&gt; command. Adding the &lt;code&gt;TOXENV&lt;/code&gt; environment variable makes it easy to specify which testenv to run.Travis runs it&amp;#39;s tasks based on the build matrix of environment variables and language versions, allowing us to run each testenv asynchronously and reporting the results for each version separately.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Mocking Context Managers in Python</title><link href="https://jsatt.com/blog/mocking-context-managers-in-python/" rel="alternate"/><published>2014-02-28T22:33:13+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/mocking-context-managers-in-python/</id><summary type="html">
&lt;p&gt;I&amp;#39;ve often found Python&amp;#39;s context managers to be pretty useful. They make a nice interface that can handle starting and ending of temporary things for you, like opening and closing a file.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;f = open(&amp;#39;myfile.txt&amp;#39;, &amp;#39;w&amp;#39;)
try:
for row in records:
f.write(row)
finally:
f.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;can be replaced with&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;with open(&amp;#39;myfile.txt&amp;#39;, &amp;#39;w&amp;#39;) as f:
for row in records:
f.write(row)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This last week I was working with the ZipFile module and wanted to use it&amp;#39;s context manger interface, but I ran into a little confusion when it came to unit testing. After a little better understanding of how context managers work, I figured out that the &lt;code&gt;__enter__&lt;/code&gt; and &lt;code&gt;__exit__&lt;/code&gt; methods are what really makes a context handler. As &lt;a href="http://pymotw.com/2/contextlib/"&gt;explained at PyMOTW&lt;/a&gt;, when you invoke &lt;code&gt;with&lt;/code&gt; on a class, __enter__ is called and should return an object to be used in the context (&lt;code&gt;f&lt;/code&gt; in the above example), the code within the block is executed, and &lt;code&gt;__exit__&lt;/code&gt; is called no matter the outcome of the block. So both of these would be roughly equivalent, assuming &lt;code&gt;do_stuff&lt;/code&gt; doesn&amp;#39;t raise an exception.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;with Context(foo) as bar:
do_stuff(bar)
c = Context(foo)
bar = c.__enter__()
try:
do_stuff(bar)
finally:
c.__exit__(None, None, None)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this understanding, here is the solution to my mocking problem using PyMox.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;#module/tasks.py
def zip_it_up(filename):
with ZipFile(filename, &amp;#39;w&amp;#39;) as f:
for file in FILES:
f.write(file.path, file.name)
# tests.py
... # setup and stuff is up here somewhere
def test_building_zipfile(self):
self.mock.StubOutWithMock(module.tasks, &amp;#39;ZipFile&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp; mock_zip = self.mock.CreateMockAnything()
&amp;nbsp;&amp;nbsp;&amp;nbsp; module.tasks.ZipFile(&amp;#39;/tmp/export.zip&amp;#39;, &amp;#39;w&amp;#39;).AndReturn(mock_zip)
&amp;nbsp;&amp;nbsp;&amp;nbsp; mock_zip.__enter__().AndReturn(mock_zip)
&amp;nbsp;&amp;nbsp;&amp;nbsp; mock_zip.write(&amp;#39;/tmp/export-1.xml&amp;#39;, &amp;#39;export-1.xml&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp; mock_zip.write(&amp;#39;/tmp/export-2.xml&amp;#39;, &amp;#39;export-2.xml&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp; mock_zip.__exit__(None, None, None)
self.mock.ReplayAll()
zip_it_up()
self.mock.VerifyAll()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mocking out ZipFile allows us to return a mock object from it&amp;#39;s instantiation. We can then set the expectation that __enter__ will be called on the instance, returning the instance itself, expecting &lt;code&gt;write&lt;/code&gt; to be called twice on the instance and finally __exit__ to be called. The three arguments of &lt;code&gt;None&lt;/code&gt; here are to indicate that an exception isn&amp;#39;t expected. If the code inside the context block were to raise an exception, these arguments would be the &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;traceback&lt;/code&gt; as returned by &lt;code&gt;raise&lt;/code&gt;. In the event you are testing for an exception, these arguments should be set accordingly when setting expectations.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Abusing Django Rest Framework Part 2: Non-rate-based Throttling</title><link href="https://jsatt.com/blog/abusing-django-rest-framework-part-2-non-rate-based-throttling/" rel="alternate"/><published>2014-02-19T16:43:17+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/abusing-django-rest-framework-part-2-non-rate-based-throttling/</id><summary type="html">
&lt;p&gt;Anyone running an API that can be reached by the outside world should most definitely be concerned that someone might pummel their server by making a massive amount of requests to that one endpoint that requires a bunch of on-the-fly calculations. Enter Django Rest Framework&amp;#39;s throttling. It allows you to easily configure the framework to stop allowing requests from a user once they&amp;#39;ve made so many requests in a period of time. Whether you&amp;#39;re concerned about requests over a sustained period of time or in short bursts, rate limiting with throttles will handle it.&lt;/p&gt;
&lt;p&gt;However, there are times you might need to limit based on something less consistent than number of calls per minute or day. One example we we ran into was clients submitting requests for a process that requires a lot of human work to complete, and the possibility that a large number of these requests might mean that the client doesn&amp;#39;t actually understand the real purpose of the feature, or an indicator of a bigger problem for that client. If you can limit these clients to a certain number of outstanding requests at any given time, this allows for the time for our client contacts to process the appropriate requests or contact them if the requests are inappropriate.&lt;/p&gt;
&lt;p&gt;While the throttles provided by Rest Framework are all about rate limiting, writing a custom throttle to do this is pretty simple.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;### throttles.py
from django.db.models import Count
from rest_framework.throttling import BaseThrottle
from rest_framework.exceptions import Throttled
from .models import Inspection
class InspectionThrottle(BaseThrottle):
def allow_request(self, request, view):
inspections = Inspection.object.filter(client=view.client)
if inspections &amp;lt; 15:
return True
raise Throttled(detail=(
&amp;quot;You have reached the limit of 15 open requests. &amp;quot;
&amp;quot;Please wait until your existing requests have been &amp;quot;
&amp;quot;evaluated before submitting additional disputes. &amp;quot;))
###views.py
class InspectionCreateView(CreateAPIView):
model = Inspection
throttle_classes = InspectionThrottle&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By simply defining an &lt;code&gt;allow_request&lt;/code&gt; method you are free to use whatever logic you want to throttle requests, so long as you return &lt;code&gt;True&lt;/code&gt; to allow the request and raise the &lt;code&gt;Throttled&lt;/code&gt; exception to deny the request. &lt;code&gt;Throttled&lt;/code&gt; also accepts a kwarg of wait which will set the &lt;code&gt;X-Throttle-Wait-Second&lt;/code&gt; header for notifying the client how long to wait before trying again. This is mostly just useful for rate limiting, but maybe you&amp;#39;ll be clever and have a reason to use it.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>About</title><link href="https://jsatt.com/about/" rel="alternate"/><published>2014-02-10T22:36:53+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/about/</id><summary type="html">
&lt;div class="row"&gt;
&lt;div class="col-md-4 pull-right"&gt;&lt;img src="/media/me-again.jpg" style="float:right; height:364px; width:300px" title="Jeremy Satterfield" /&gt;&lt;/div&gt;
&lt;div class="col-md-8"&gt;
&lt;p&gt;I&amp;#39;m a web developer and homebrewer from Tulsa, OK with an affinity toward open-source. I work primarily in Python, Django, Javascript and and related languages such as CoffeeScript, CSS and Stylus. As a member of &lt;a href="http://tulsawebdevs.org" target="_blank"&gt;TulsaWebDevs&lt;/a&gt;, &lt;a href="http://codefortulsa.org" target="_blank"&gt;CodeforTulsa&lt;/a&gt;, and &lt;a href="http://civicninjas.org"&gt;Civic Ninjas&lt;/a&gt;, I also work on many civic-minded projects.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</summary></entry><entry><title>Abusing Django Rest Framework Part 1: Non-Model Endpoints</title><link href="https://jsatt.com/blog/abusing-django-rest-framework-part-1-non-model-endpoints/" rel="alternate"/><published>2014-01-29T18:32:43+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/abusing-django-rest-framework-part-1-non-model-endpoints/</id><summary type="html">
&lt;p&gt;When working with &lt;a href="http://www.django-rest-framework.org/" target="_blank"&gt;Django Rest Framwork&lt;/a&gt; a few months back, there were a few road blocks that we ran into. Rest Framwork is awesome with most models for providing a simple CRUD API, in any (or multiple) serializations, with authentication and permissions. Sometimes, however, things aren&amp;#39;t so simple. Things get ugly. Framworks get abused.&lt;/p&gt;
&lt;p&gt;This is the first of a few posts looking at some of the ways Rest Framwork can be beaten into submission. Some of them may be the way the framework intended them to be implement, but non-obvious, while others were never intended, are probably bad ideas, and should be avoided at all costs (except when the needs of the business require it, of course).&lt;/p&gt;
&lt;h2&gt;Creating Endpoints to Trigger Non-model Processes&lt;/h2&gt;
&lt;p&gt;Let&amp;#39;s start out easy. As I mentioned Rest Framework is great at working with models, but it can also be easily used for things other than retrieving and saving models. Say for instance you have an email form on your site for sharing a post. Using AJAX posting of those fields makes for a better experience for your user and keeps them browsing the page longer.&lt;/p&gt;
&lt;p&gt;While it&amp;#39;s not exactly clear, the &lt;code&gt;APIView&lt;/code&gt; allows for this. Most of the documentation for &lt;code&gt;APIView&lt;/code&gt; is spent doing model retrievals that are made easier when you start using &lt;code&gt;GenericAPIView&lt;/code&gt; and those that inherit from it. However, using the dispatch methods (&lt;code&gt;post&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt;, etc.), just like a Django class-based view, our example above becomes pretty easy.&lt;/p&gt;
&lt;pre&gt;
&lt;code class="language-python"&gt;from rest_framework import views
from rest_framework.response import Response
class ShareView(views.APIView):
permission_classes = []
def post(self, request, *args, **kwargs):
email = request.DATA.get(&amp;#39;email&amp;#39;, None)
url = request.DATA.get(&amp;#39;url&amp;#39;, None)
if email and url:
share_url(email, url)
return Response({&amp;quot;success&amp;quot;: True})
else:
return Response({&amp;quot;success&amp;quot;: False})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In these dispatch methods you can easily access the deserialized posted data (via &lt;code&gt;request.DATA&lt;/code&gt;), run your custom code (&lt;code&gt;share_url()&lt;/code&gt; in this case) and return a serialized response (passing your data to &lt;code&gt;Response&lt;/code&gt;). If you&amp;#39;re arbitrary code execution applies to a model object, maybe a method call, but doesn&amp;#39;t including retrieving or updating that model, simply inherit from the &lt;code&gt;GenericAPIView&lt;/code&gt; instead the &lt;code&gt;APIView&lt;/code&gt; and use the &lt;code&gt;get_object&lt;/code&gt; method of the view.&lt;/p&gt;
&lt;p&gt;The one caveat when using &lt;code&gt;APIView&lt;/code&gt; is to watch what permissions are applied to the view. Since it is not associated with a model, any permission that expects a model object to be available, like the &lt;code&gt;DjangoModelPermission&lt;/code&gt; that is applied by default, will fail, so you should be explicit about permissions.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Finding Un-mocked HTTP requests in Python tests with Nose</title><link href="https://jsatt.com/blog/finding-un-mocked-http-requests-in-python-tests-with-nose/" rel="alternate"/><published>2014-01-16T18:40:58+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/finding-un-mocked-http-requests-in-python-tests-with-nose/</id><summary type="html">
&lt;p&gt;I&amp;#39;m a big fan of proper unit testing with mocking. So I&amp;#39;m pretty disappointed when we run across a unit that is not only not mocked properly, but results in real-world consequences outside the testing environment. One case we&amp;#39;ve run into couple of times now is tests that are making actual outbound HTTP requests to remote servers. Since clients don&amp;#39;t necessarily like getting fake information posted to their servers every time you run tests, I figured this was a good time to get to the bottom of this.&lt;/p&gt;
&lt;p&gt;I caught this particular case while researching another broken test and noticed that one particular test was hanging for several second before passing every time. After digging in, I found that the view being tested called a method, which called a method, which executed a task that included a &lt;code&gt;requests.post()&lt;/code&gt; call. It was quick to mock out the first method call that spawned it all, but I was worried about next time we test against that view and forget the mock again.&lt;/p&gt;
&lt;p&gt;After a little research I found that Nose provides the ability to run package and module level setup and teardown functions. Simply add &lt;code&gt;setUpPackage&lt;/code&gt;, &lt;code&gt;setUpModule&lt;/code&gt;, &lt;code&gt;tearDownPackage&lt;/code&gt; and &lt;code&gt;tearDownModule&lt;/code&gt; functions to the &lt;code&gt;__init__.py&lt;/code&gt; of the package or module appropriately and you&amp;#39;re free to do any setup and cleanup you need for the package or module as a whole. This can include setting up fixtures, test databases, or, as I discovered, mocking. Placing the following code in the &lt;code&gt;__init__.py&lt;/code&gt; of my project, quickly identified another instance where we were making outbound requests that we weren&amp;#39;t catching.&lt;/p&gt;
&lt;pre class="line-numbers"&gt;
&lt;code class="language-python"&gt;pkg_mox = None
def setUpPackage():
global pkg_mox
import urllib
import urllib2
import mox
import requests
pkg_mox = mox.Mox()
pkg_mox.StubOutWithMock(requests.api.sessions.Session, &amp;#39;send&amp;#39;)
pkg_mox.StubOutWithMock(urllib.URLopener, &amp;#39;open&amp;#39;)
pkg_mox.StubOutWithMock(urllib2.OpenerDirector, &amp;#39;open&amp;#39;)
pkg_mox.ReplayAll()
def tearDownPackage():
pkg_mox.VerifyAll()
pkg_mox.UnsetStubs()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cases where requests were being made, but not properly mocked, raised the &amp;quot;Unexpected method called&amp;quot; exception you&amp;#39;d expect, including the traceback to the offending test.&lt;/p&gt;
&lt;p&gt;Through the years, we&amp;#39;ve gone through a few iterations on how we like making our http calls. While we&amp;#39;ve settled on the requests library for now you&amp;#39;re always going to be dealing with legacy code. This flow catches calls to all three major request libraries, just in case. Mocking the underlying methods of the libraries catches all of the common paths for executing requests for each, while still allowing those common cases to be properly mocked in your test code.&lt;/p&gt;
&lt;p&gt;I&amp;#39;m certain there are many other libraries that this could be helpful for. Now let&amp;#39;s get to writing better unit tests.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Decorators vs Mixins for Django Class-Based Views</title><link href="https://jsatt.com/blog/decorators-vs-mixins-for-django-class-based-views/" rel="alternate"/><published>2014-01-13T20:21:58+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/decorators-vs-mixins-for-django-class-based-views/</id><summary type="html">
&lt;p&gt;I&amp;#39;ve been huge fan of Django&amp;#39;s class-based views (CBVs) since I first tried them out in Django 1.4.&amp;nbsp; While they&amp;#39;re much more complicated then the classic function based views, once you understand how they work, they&amp;#39;re much more powerful, flexible and allow for DRYer code. I highly recommend anyone who hasn&amp;#39;t delved into CBVs take a look at &lt;a href="http://ccbv.co.uk/"&gt;Class Class-based&lt;/a&gt;&lt;a href="http://ccbv.co.uk/"&gt; views&lt;/a&gt; or &lt;a href="https://godjango.com/15-class-based-views-part-1-templateview-and-redirectview/"&gt;GoDjango&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, one of the early issues I ran into was for views that required Django user permissions. With function based views, Django&amp;#39;s auth application provides decorators to check that users are logged in, have a specific permission, or pass other custom checks the developer can provide. You simply add the decorator to the view...&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;@permission_required(&amp;#39;auth.change_user&amp;#39;)
def user_list(request):
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now any user that doesn&amp;#39;t have the required permission is redirected to the login page.&lt;/p&gt;
&lt;p&gt;For CBVs though, it&amp;#39;s not quite that simple, it&amp;#39;s the dispatch method that begins the process that you&amp;#39;re used to picking up from with function based views. This means that to apply a decorator to the CBV, you have a couple of options.&lt;/p&gt;
&lt;p&gt;You can override the dispatch method to apply the decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class UserListView(ListView):
model = User
@my_custom_decorator
def dispatch(self, request, *args, **kwargs):
return super(UserListView, self).dispatch(request, *args, **kwargs)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could write a reusable class view decorator:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def class_view_decorator(function_decorator):
def deco(View):
View.dispatch = method_decorator(function_decorator)(View.dispatch)
return View
return deco
@class_view_decorator(my_custom_decorator)
class UserListView(ListView):
model = User&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This second option is the one that I&amp;#39;ve lived with for a long time. It&amp;#39;s relatively clean and DRY and not much different than what you&amp;#39;re used to seeing with function views.&lt;/p&gt;
&lt;p&gt;But recently I was pointed toward &lt;a href="https://github.com/brack3t/django-braces"&gt;Django Braces&lt;/a&gt;. It&amp;#39;s a library of commonly (and not-so-commonly) needed view decorator-like functionality in the form of mixins. I immediately realized that when thinking about the functionality these decorators provide, I was stuck in the old way of doing things rather than looks at these views from an OOP perspective.&lt;/p&gt;
&lt;p&gt;Writing these decorators as mixins instead, makes them more flexible, extensible and DRYer, just like the move from function views to CBVs. You&amp;#39;re able to build a base mixin that others can inherit from and extend. What would have been arguments to the decorator can are much cleaner as class attributes for the view that&amp;#39;s extending it. You can even have views that apply affect the mixin on a one-off basis. Take for instance the &amp;lt;code&amp;gt;UserCheckMixin&amp;lt;/code&amp;gt; below.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class UserCheckMixin(object):
user_check_failure_path = &amp;#39;&amp;#39; # can be path, url name or reverse_lazy
def check_user(self, user):
return True
def user_check_failed(self, request, *args, **kwargs):
return redirect(self.user_check_failure_path)
def dispatch(self, request, *args, **kwargs):
if not self.check_user(request.user):
return self.user_check_failed(request, *args, **kwargs)
return super(UserCheckMixin, self).dispatch(request, *args, **kwargs)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I now simply add the mixin to a view:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class UserListView(UserCheckMixin, ListView):
model = User
user_check_failure_path = &amp;#39;auth_login&amp;#39;
def check_user(self, user)
# do some check against the user here and return True or False&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can extend it to build a more useful mixin:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;class PermissionRequiredMixin(UserCheckMixin):
user_check_failure_path = &amp;#39;auth_login&amp;#39;
permission_required = None
def check_user(self, user):
return user.has_perm(self.permission_required)
class UserListView(PermissionRequredMixin, ListView):
model = User
permission_required = &amp;#39;auth.change_user&amp;#39;
user_check_failure_path = &amp;#39;auth_login&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, once you&amp;#39;ve defined the &lt;code&gt;PermissionRequiredMixin&lt;/code&gt;, it&amp;#39;s much cleaner, more object-oriented and I would argue more pythonic.&lt;/p&gt;
&lt;p&gt;If you&amp;#39;re relying on built-in decorators, the &lt;code&gt;class_view_decorator&lt;/code&gt; is a pretty helpful tool to have in your codebase. But for anything custom you may be writing, it really is hard to beat a mixin.&lt;/p&gt;</summary><category term="development"/></entry><entry><title>Fresh start</title><link href="https://jsatt.com/blog/fresh-start/" rel="alternate"/><published>2014-01-11T22:35:43+00:00</published><updated>2022-02-25T18:18:59-06:00</updated><author><name>Jeremy Satterfield</name></author><id>https://jsatt.com/blog/fresh-start/</id><summary type="html">
&lt;p&gt;This is going to be a fresh attempt to start blogging again on a regular basis. I plan to share development and brewing experiences, open-sourcing in both cases whenever possible.&lt;/p&gt;</summary></entry></feed>