<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Python on Алексей Пахунов</title>
    <link>https://blog.not-a-kernel-guy.com/tags/python/</link>
    <description>Recent content in Python on Алексей Пахунов</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>ru-ru</language>
    <lastBuildDate>Tue, 20 Nov 2018 22:36:35 -0800</lastBuildDate>
    <atom:link href="https://blog.not-a-kernel-guy.com/tags/python/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Волшебство Питона</title>
      <link>https://blog.not-a-kernel-guy.com/2018/11/20/python-magic/</link>
      <pubDate>Tue, 20 Nov 2018 22:36:35 -0800</pubDate>
      <guid>https://blog.not-a-kernel-guy.com/2018/11/20/python-magic/</guid>
      <description>&lt;p&gt;Мне все-таки не понятно как живут большие проекты, написанные на Питоне. Он же&#xA;как песок. Пока проект небольшой - все классно. Быстро накидали горку мокрого&#xA;песка, лопаткой обхлопали для придания формы и все дела. Знай только что брызгай&#xA;водой своевременно. Когда проект разрастается, то красивый и уютный песчанный&#xA;замок превращается в минное поле. Отрефакторил подвал - крыша отвалилась.&#xA;Поправил крышу - окна слиплись в один комок.&lt;/p&gt;&#xA;&lt;p&gt;Непонятно как все это счастье держать в одной куче. Неужели все живут за счет&#xA;100% покрытия тестами? Не верю. Или все на PyCharm сидят?&lt;/p&gt;&#xA;&lt;p&gt;Расскажу про очередной прикол Питона. Итак есть простой код:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;(factories):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Convert a list of factorie into a list of objects.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [factory() &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; factory &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; factories]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cleanup&lt;/span&gt;(objects):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Clean up objects in the reversed order of their creation.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; obj &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; reversed(objects):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        obj&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;init()&lt;/code&gt; берет список фабрик и отдает список объектов, созданных фабриками.&#xA;&lt;code&gt;cleanup()&lt;/code&gt; чистит созданные объекты в порядке, противоположном порядку&#xA;создания. Нам требуется написать тест, который проверяет, что методы &lt;code&gt;cleanup()&lt;/code&gt;&#xA;вызываются в правильном порядке:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; itertools &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; permutations&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; mock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_cleanup&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Verify cleanup() order.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# Try all permitations of initialization order. &lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; init_order &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; permutations(range(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cleanup_order &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;factory&lt;/span&gt;(index):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cleanup&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Record the clean up order.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                cleanup_order&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(index)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create&lt;/span&gt;():&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Return a mock implementing cleanup().&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                obj &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mock&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Mock&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                obj&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;cleanup &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; mock&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Mock(side_effect&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;cleanup)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; obj&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; create&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Create objects in the desired order.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        objects &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; init([factory(index) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; index &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; init_order])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        cleanup(objects)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# Verify that the object were cleaned up in the reveresed&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# order of their creation.&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; cleanup_order &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; list(reversed(init_order))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Разберу логику по кускам. Тест перебирает все возможные кобинации порядка&#xA;создания объектов:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; init_order &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; permutations(range(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;)):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Декоратор &lt;code&gt;factory()&lt;/code&gt; возвращает фабрику &lt;code&gt;create()&lt;/code&gt;, которая в свою учередь&#xA;создает объект с методом &lt;code&gt;cleanup()&lt;/code&gt;. Для создания объекта на коленке&#xA;используется &lt;a href=&#34;https://docs.python.org/3/library/unittest.mock.html&#34;&gt;&lt;code&gt;Mock&lt;/code&gt;&lt;/a&gt;. Релизация &lt;code&gt;cleanup()&lt;/code&gt; запоминает порядок вызова в&#xA;&lt;code&gt;cleanup_order&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;init()&lt;/code&gt; создает объекты в заданном порядке, &lt;code&gt;cleanup()&lt;/code&gt; - чистит:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;objects &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; init([factory(index) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; index &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; init_order])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cleanup(objects)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Наконец, в самом конце мы проверяем, что порядок очистки противоположен порядку&#xA;создания:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; cleanup_order &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; list(reversed(init_order))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Все просто, не так ли? Запускаем тест и получаем облом:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;               assert cleanup_order == list(reversed(init_order))&#xA;E     assert [2, 2, 2] == [2, 1, 0]&#xA;E       At index 1 diff: 2 != 1&#xA;E       Use -v to get the full diff&#xA;&#xA;&#xA;rabbit_test.py:41: AssertionError &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Заядлые питонщики давно раскусили, в чем проблема. А вы сможете найти ошибку не&#xA;заглядывая в ответ?&lt;/p&gt;</description>
    </item>
    <item>
      <title>Ну-ка, еще разок</title>
      <link>https://blog.not-a-kernel-guy.com/2018/11/08/retrying/</link>
      <pubDate>Thu, 08 Nov 2018 21:57:46 -0800</pubDate>
      <guid>https://blog.not-a-kernel-guy.com/2018/11/08/retrying/</guid>
      <description>&lt;p&gt;Питоний пакет &lt;a href=&#34;https://pypi.org/project/retrying/&#34;&gt;retrying&lt;/a&gt; - универсальная заплатка для кода, который может&#xA;поломаться из-за внешних причин. Например, &lt;a href=&#34;https://docs.python.org/3.7/library/socket.html#socket.socket.connect&#34;&gt;&lt;code&gt;socket.connect()&lt;/code&gt;&lt;/a&gt; может&#xA;закончится ошибкой из-за непредвиденной ситуации в сети. Заворачиваем вызов в&#xA;&lt;code&gt;@retry&lt;/code&gt; и бац! Все работает.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-py&#34; data-lang=&#34;py&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@retry&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;create_connection&lt;/span&gt;(address):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    s &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; socket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;socket(socket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AF_INET, socket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;SOCK_STREAM)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; socket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect(address)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Однако бездумное применение &lt;code&gt;@retry&lt;/code&gt;, как нетрудно догадаться, приводит&#xA;проблемам. Самый известный пример - вложенные &lt;code&gt;@retry&lt;/code&gt;.&#xA;&lt;code&gt;@retry(stop_max_attempt_number=3)&lt;/code&gt; вложенный в другой&#xA;&lt;code&gt;@retry(stop_max_attempt_number=3)&lt;/code&gt; дает 9 повторов. Три уровня вложенности -&#xA;27 повторов.&lt;/p&gt;&#xA;&lt;p&gt;Один раз использование &lt;code&gt;@retry&lt;/code&gt; привело к тому, что IT заблокировало мою учетную&#xA;запись. Банальная история - был некий скрипт, который при запуске спрашивал&#xA;логин и пароль для авторизации. В один прекрасный день некто (не будем&#xA;показывать пальцем, хотя это был Слонёнок) усовершенствовал скрип, завернув&#xA;сетевой вызов в &lt;code&gt;@retry&lt;/code&gt;. Отличная идея, за исключением того, что ошибка&#xA;авторизации приводила к повторному запросу с тем же неправильным именем и&#xA;паролем. После определенного числа попыток контроллер домена решил, что кто-то&#xA;пытается подобрать пароль к учетной записи и заблокировал её.&lt;/p&gt;&#xA;&lt;p&gt;Мораль истории - знайте свои ошибки в лицо и используйте параметр&#xA;&lt;code&gt;retry_on_exception&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
