<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Саша Демура</title><generator>teletype.in</generator><description><![CDATA[👨‍💼 Software Test Automation Engineer
📍 Tbilisi, Georgia]]></description><image><url>https://img4.teletype.in/files/3b/65/3b6544f1-bd48-42c5-9047-b11e105a164e.png</url><title>Саша Демура</title><link>https://teletype.in/@dmrlx</link></image><link>https://teletype.in/@dmrlx?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/dmrlx?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/dmrlx?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Fri, 29 May 2026 17:42:17 GMT</pubDate><lastBuildDate>Fri, 29 May 2026 17:42:17 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@dmrlx/batumi-beginning</guid><link>https://teletype.in/@dmrlx/batumi-beginning?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/batumi-beginning?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>Коренной батумец в первом поколении.</title><pubDate>Sun, 02 Oct 2022 14:03:04 GMT</pubDate><category>офлайн</category><description><![CDATA[<img src="https://img3.teletype.in/files/65/69/6569fccb-dfa5-4b10-8ec7-9d7ea3391c47.jpeg"></img>Вчера эффектно переехал в Батуми.]]></description><content:encoded><![CDATA[
  <p id="BuP7">Вчера эффектно переехал в Батуми.</p>
  <p id="rXBx">Всё дело в том, что в последнее время (ещё до начала мобилизации) цены на жильё в Тбилиси были какими-то совершенно безумными. После начала — просто стали ещё более безумными. После 2х дней поисков жилья в Тбилиси появилось чёткое понимание, что найти что-либо вменяемое за вменяемые деньги в столице не выйдет и нужно менять критерии поиска. </p>
  <p id="vD5k">Пока искал квартиру в Тбилиси была пара смешных историй. Первая — риэлтер, которая накричала на меня и &quot;обозвала&quot; претенциозным потому что якобы по вине приезжих цены так сильно взлетели, а теперь эти приезжие не хотят за эти деньги снимать. При этом она дважды сказала, что никто не хочет сдавать белорусам и чтобы я ехал обратно. Было смешновато. Вторая история похожая, нашлась одна квартира относительно недалеко от центра (в первую очередь относительно моей первой квартиры; если интересно, погуглите Ист Поинт в Тбилиси и оцените как далеко я жил) за $550. Новый дом, вменяемый ремонт, хозяйку всё устраивало. Когда я попросил реэлтера договориться о просмотре, хозяева внезапно передумали и сказали, что не хотят сдавать русским, белорусам и украинцам. На моё вполне резонное &quot;А КОМУ, БЛЯТЬ, ОНИ ХОТЯТ ЕЁ СДАВАТЬ ЗА ТАКИЕ ДЕНЬГИ?!&quot; риэлтер ответила &quot;Я тоже поинтересовалась, они ответили, что американцам.&quot; Это была последняя капля.</p>
  <p id="EMIi">Плюнул на всё, спросил у батумских знакомых как там принято искать квартиры, мне ответили, что пишешь в специализированные телеграм чятики и к тебе сами придут. Так и сделал. В итоге за приблизительно 15 минут нашёл двухкомнатную квартиру за $450 практически в центре Батуми. Для сравнения, то, что мне предлагали в Тбилиси — окраина города (аналог Каменной Горки в Минске, если знаете) за $650+. Было это ровно в день начала мобилизации в России, 21го сентября. Очень вовремя я, да.</p>
  <p id="3s64">Так в итоге я со всеми своими пожитками (чемодан, рюкзак, монитор и две коробки всякого барахла) погрузился в Toyota Camry (кстати, очень рекомендую в Грузии сервис по аренде авто с водителем <a href="https://gotrip.ge/" target="_blank">gotrip</a>) и за 6 часов и $100 комфортно добрался до точки бэ. </p>
  <p id="Fll5">Пока смешанные чувства. В Тбилиси жил на окраине и там был вид на бараки, пригорок и взлетающие/садящиеся самолёты. Типа вот так.</p>
  <figure id="PM0a" class="m_column">
    <img src="https://img3.teletype.in/files/65/69/6569fccb-dfa5-4b10-8ec7-9d7ea3391c47.jpeg" width="4032" />
  </figure>
  <p id="4O3B">А тут кругом жилая застройка и моря не видно. Хотя нет, я вру. Видно, кнчн.</p>
  <figure id="AbRU" class="m_column">
    <img src="https://img3.teletype.in/files/a4/5d/a45dad31-05f5-44ea-9a20-f30b643502df.jpeg" width="3024" />
    <figcaption>Таааак...</figcaption>
  </figure>
  <figure id="yRza" class="m_column">
    <img src="https://img1.teletype.in/files/44/cc/44cca9d1-4a4b-429a-969a-f48d7ff4ba1f.jpeg" width="3024" />
    <figcaption>Погодите, кажется...</figcaption>
  </figure>
  <figure id="TQNj" class="m_column">
    <img src="https://img1.teletype.in/files/84/7f/847fb33a-b5ab-4f18-a665-6bc534ec6a53.jpeg" width="3024" />
    <figcaption>ДА!!! С трёхкратным зумом вообще офигенно, почти на пляже.</figcaption>
  </figure>
  <p id="HNRb">Но жизнь в центральной части города — это, кнчн, прелесть что такое. Ну и возможность гулять у моря это тоже очень круто. Короче, пока вот только разобрал вещи, намутил себе рабочее место и изучаю окрестности.</p>
  <p id="2Rv1">Будем посмотреть.</p>
  <p id="dYJu">Stay tuned и Слава Украине!</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/i-snova-zdravstvujte</guid><link>https://teletype.in/@dmrlx/i-snova-zdravstvujte?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/i-snova-zdravstvujte?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>И снова здравствуйте.</title><pubDate>Thu, 29 Sep 2022 19:33:52 GMT</pubDate><category>офлайн</category><description><![CDATA[Конец весны / начало лета выдались у меня совершенно трешовыми. По личным причинам. Было не до блогов и не до чего. Не хочу рассказывать, но правда был ад.]]></description><content:encoded><![CDATA[
  <p id="hQ90">Конец весны / начало лета выдались у меня совершенно трешовыми. По личным причинам. Было не до блогов и не до чего. Не хочу рассказывать, но правда был ад.</p>
  <p id="aJ98">Сейчас уже конец сентября и я снова более чем функционален. Жив, здоров, немного подразжирел (с чем начал более-менее активно бороться, но ещё нужно бы подправить пищевые привычки), закупился разной, иногда совершенно новой для меня, техникой (собираюсь рассказать о своём опыте использования в ближайшее время). Крч, можно сказать, что всё в порядке.</p>
  <p id="yx7d">На днях переезжаю из Тбилиси в Батуми. Это очень интересная и волнительная история, но расскажу как-нить потом, когда факт свершится. Пока до конца не верю, что делаю это и даже считаю в некотором смысле везением. Говорят, Батуми в осенне-зимний период мало приятный город, но слышал и что там всё же достаточно солнечно и может не так всё и страшно. Поглядим. В любом случае, будет вряд ли хуже, чем моя текущая жизнь в тбилисском районе Варкетили.</p>
  <p id="DBAz">Такие в общем и целом дела. </p>
  <p id="hIvB">Stay tuned и Слава Украине!</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/I_did</guid><link>https://teletype.in/@dmrlx/I_did?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/I_did?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>Я сделал!</title><pubDate>Fri, 13 May 2022 21:07:18 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a4/5e/a45e9b72-649a-46c7-b03d-e67cce1938af.png"></media:content><category>камплюхтеры</category><description><![CDATA[<img src="https://img3.teletype.in/files/aa/1e/aa1e8c28-3e28-46b8-8ad5-2fbcf7473eab.png"></img>Крч. Я сделал, чуваки.]]></description><content:encoded><![CDATA[
  <figure id="lQmo" class="m_column">
    <img src="https://img3.teletype.in/files/aa/1e/aa1e8c28-3e28-46b8-8ad5-2fbcf7473eab.png" width="1186" />
  </figure>
  <p id="1Mvk">Крч. Я сделал, чуваки.</p>
  <p id="psi8">Я сделал две абсолютно крутейшие штуки, которыми буду гордиться в ближайшие пару недель. Сегодня какой-то на удивление продуктивный день.</p>
  <p id="pPEd">Я обожаю эту работу! Рок-н-ролл! 🤘</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/victory_day</guid><link>https://teletype.in/@dmrlx/victory_day?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/victory_day?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>💭 День победы</title><pubDate>Mon, 09 May 2022 09:55:06 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/e0/b9/e0b90079-a8d0-4c9a-b2f4-51a8a720ee84.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/77/1a/771ae22f-df67-4644-9dd7-74fb7cc986cb.jpeg"></img>24 февраля 2022 года мир сломался. Во всяком случае для жителей Украины, России и Беларуси. Для кого-то в большей степени, для кого-то в меньшей, кто-то ещё не осознал до конца масштаб возможных последствий и пребывает в счастливом неведении. Но так или иначе зацепило или зацепит всех.]]></description><content:encoded><![CDATA[
  <figure id="BmTy" class="m_original">
    <img src="https://img4.teletype.in/files/77/1a/771ae22f-df67-4644-9dd7-74fb7cc986cb.jpeg" width="1200" />
  </figure>
  <p id="jLru">24 февраля 2022 года мир сломался. Во всяком случае для жителей Украины, России и Беларуси. Для кого-то в большей степени, для кого-то в меньшей, кто-то ещё не осознал до конца масштаб возможных последствий и пребывает в счастливом неведении. Но так или иначе зацепило или зацепит всех.</p>
  <p id="fUH3">Где-то в начале апреля я разговаривал с поляком с моего проекта и пытался ему объяснить насколько это пиздец для любого вменяемого жителя Беларуси-Украины-России, рассказывал про мемориал &quot;Три сестры&quot; на границе этих трёх стран, насколько исторически мы всегда были связаны; да, часто воевали, конечно, но так или иначе наши истории очень тесно переплетены. Было бы странно это отрицать. И мой коллега, кажется, не очень мне поверил. В том смысле, что &quot;если всё обстоит так, как ты говоришь, то как могло случиться, то, что случилось?!&quot; </p>
  <p id="qHAV">Я не знаю.</p>
  <p id="NIwA">Я родился в 1983 году, вырос во времена, когда парады на День победы были многочисленными. Мы с одноклассниками ходили на эти парады и дарили ветеранам тюльпаны. Дядька моей матери в 16 лет пошёл на фронт, попал в концлагерь, бежал оттуда, добрался до Беларуси, попал в партизанский отряд, был ранен. Мы ездили к нему в гости поздравлять с Праздником. Он рассказывал нам об ужасах той войны. И День победы всегда был для меня праздником. А сегодня — нет. Сегодня 9 мая — повод для пропагандонов поугрожать миру ядерным оружием и проорать мантру &quot;можемповторить&quot;.</p>
  <p id="EMFC">Надеюсь, так будет не всегда и День победы снова будет Днём победы. Только уже над другими фашистами.</p>
  <p id="wFBu">Сегодня я хочу только пожелать всего самого доброго украинцам. У вас всё получится. </p>
  <p id="cSoM">Слава Україні!</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/virtualenv_usage</guid><link>https://teletype.in/@dmrlx/virtualenv_usage?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/virtualenv_usage?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>🛠 Использование virtualenv</title><pubDate>Mon, 09 May 2022 08:36:23 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/7d/34/7d34c653-b9ec-46ff-86c6-c4e0e2110e28.png"></media:content><category>камплюхтеры</category><description><![CDATA[<img src="https://img4.teletype.in/files/b2/b6/b2b6eed9-8e2a-4206-b04f-10051a96267b.jpeg"></img>В этом посте я попробую объяснить что такое virtualenv, для чего он нужен и как им пользоваться python-разработчику. Погнали.]]></description><content:encoded><![CDATA[
  <figure id="hwgx" class="m_original">
    <img src="https://img4.teletype.in/files/b2/b6/b2b6eed9-8e2a-4206-b04f-10051a96267b.jpeg" width="3882" />
  </figure>
  <p id="5saj">В этом посте я попробую объяснить что такое virtualenv, для чего он нужен и как им пользоваться python-разработчику. Погнали.</p>
  <hr />
  <p id="Pnau">🛫 Введение</p>
  <p id="FTw9">Давайте начнём с серии вопросов, которые я когда-то задавал сам себе, и ответов, которые я сегодняшний могу дать себе тогдашнему.</p>
  <p id="85T3"><strong>Вопрос</strong>: <em>Что такое virtualenv и почему мне обязательно нужно научиться им пользоваться?</em></p>
  <p id="hKTP"><strong>Ответ</strong>: <em>Virtualenv — это утилита, которая позволяет создавать изолированные виртуальные окружения для Python. По сути, она создаёт внутри вашего проекта каталог, в который кладёт нужную вам версию Python и туда же устанавливает все необходимые пакеты, которые вы будете использовать в своём приложении, фреймворке, скрипте. В результате вы получаете полностью рабочее, изолированное и, что очень важно в дальнейшем, переносимое окружение.</em></p>
  <p id="dmXI"><strong>Вопрос</strong>: <em>Изолированное?</em></p>
  <p id="TQ4D"><strong>Ответ</strong>: <em>Да, именно изолированное. Это очень важно. Если вы работаете только над одним проектом всю жизнь, то проблемы нет. Но если проектов больше одного, и вы используете в них разные наборы пакетов, то в какой-то момент уже не сможете вспомнить какому проекту какие пакеты нужны. А ещё нужно помнить о версиях, чтобы всё не рассыпалось. Можно, конечно, записывать на бумажку, но это не путь джедая.</em></p>
  <p id="Tpat"><strong>Вопрос</strong>: <em>Как этим пользоваться?</em></p>
  <p id="zo5R"><strong>Ответ</strong>: <em>Условно работу с virtualenv можно разбить на несколько этапов, каждый из которых я постараюсь подробно описать в посте</em>:</p>
  <ul id="id0X">
    <li id="dGhP"><em>Установка virtualenv</em></li>
    <li id="BDBs"><em>Создание окружения</em></li>
    <li id="C6w5"><em>Активация окружения</em></li>
    <li id="3c8M"><em>&lt;Здесь мы работаем как обычно, не обращая особого внимания на то, что мы находимся в каком-то там виртуальном окружении&gt;</em></li>
    <li id="Ocz5"><em>Перенос окружения</em></li>
    <li id="PdEJ"><em>Деактивация окружения</em></li>
  </ul>
  <p id="Zphd"><strong>Вопрос</strong>: <em>Когда нужно использовать virtualenv?</em></p>
  <p id="vg0k"><strong>Ответ</strong>: <em>Я считаю, что всегда. Дело в том, что результатом разработки любой программы является её запуск на компьютере, который отличается от вашего. Это может быть какой-то сервер или машина другого пользователя. Использование virtualenv позовляет минимизировать опасность ставшей шуткой ситуации “Ну, не знаю, у меня на компе всё работает!”</em></p>
  <h2 id="W90P">🚀 Поехали!</h2>
  <p id="En8r">Сейчас я расскажу подробно про каждый из этапов работы с virtualenv на примере. Уточню, что всё это я делаю только на Linux (Manjaro в моём случае), т.к. это моя основная рабочая система. Многое будет актуально и для Windows, но некоторые шаги будут отличаться, и придётся гуглить. Сорян.</p>
  <p id="mQxj"><strong>1. Установка virtualenv</strong></p>
  <p id="s8Ey">Не уверен на 200%, но кажется, что у меня virtualenv был установлен в Manjaro с самого начала. Если у вас не так, то установить его очень просто, используя менеджер пакетов pip:</p>
  <pre id="k93N" data-lang="bash"># pip install virtualenv</pre>
  <p id="cVyg">Обратите внимание, что первым символом идёт “#”, а это значит, что команда должна быть выполнена под root’ом. Это важно. В противном случае вы увидите сообщение, а-ля <code>ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: ‘/usr/lib/python3.8/site-packages/wcwidth-0.1.7.dist-info’</code>. Нам это не нужно, поэтому либо смените пользователя через</p>
  <pre id="JC9s" data-lang="bash">$ su</pre>
  <p id="JYfa">и после этого выполните первую команду, либо сразу выполните её с sudo:</p>
  <pre id="pJCx" data-lang="bash">$ sudo pip install virtualenv</pre>
  <p id="kkvj">Символ <code>$</code> в начале говорит о том, что команда выполняется из-под не привилегированного пользователя.</p>
  <p id="AhoC">Вряд ли что-то пошло не так, и после установки вы можете проверить, что virtualenv установлен и посмотреть какая у вас версия:</p>
  <pre id="K87v" data-lang="bash">$ virtualenv --version</pre>
  <p id="nxc3">У меня 16.7.9, но если у вас другая, вы вряд ли почувствуете разницу.</p>
  <p id="qlE7"><strong>2. Создание окружения</strong></p>
  <p id="Jxfx">Переходим к работе непосредственно с virtualenv. Например, вы начинаете проект my_project, который будет храниться у вас в домашней директории в директории Projects, т.е. <code>/home/&lt;your_username&gt;/Projects/my_project</code>. В моём случае это <code>/home/dmrlx/Projects/my_project</code>. Переходим в эту директорию:</p>
  <pre id="Xu6L" data-lang="bash">$ cd /home/dmrlx/Projects/my_project</pre>
  <p id="O3Pw">Теперь создаём в этой папке окружение. У меня в системе установлено два интерпретатора: 2.7.17 и 3.8.0. Например, установим virtualenv с Python 3.8:</p>
  <pre id="rmlh" data-lang="bash">$ virtualenv venv --python=python3.8</pre>
  <p id="GoVy">“venv” в данном случае — это название директории, в которой будет развёрнуто ваше виртуальное окружение, по сути его имя. Может быть любым другим, но, если честно, ни разу не встречал, чтобы кто-то писал что-то кроме venv. Возможно, разные имена имеют смысл, если у вас одновременно десяток проектов и нужно избежать путаницы.</p>
  <p id="VC5C">Часть после “venv” вообще можно опустить. В этом случае виртуальное окружение установится с той версией, которая задана у вас в системе по умолчанию.</p>
  <p id="SgiU">Если теперь мы посмотрим на содержимое директории “my_project” (в которой сейчас находимся), то увидим, что в ней действительно появилась директория “venv”, а внутри неё, в свою очередь, ещё три: “bin”, “include” и “lib”.</p>
  <pre id="zgpf" data-lang="bash">$ ls venv 

bin include lib</pre>
  <p id="WQr6">Круто! Пока всё идёт по плану.</p>
  <p id="aBNQ"><strong>3. Активация окружения</strong></p>
  <p id="R2OO">Окружение создано. Теперь чтобы его использовать мы должны выполнить активацию. Это значит, что мы “переключаем контекст” выполнения команд связанных с python на виртуальное окружение. Если проще, то после активации, выполняя любой python скрипт, мы будем использовать python и установленные пакеты не из системы, а из нашего виртуального окружения.</p>
  <p id="mX0E">Выполним следующее:</p>
  <pre id="rWII" data-lang="bash">$ source venv/bin/activate</pre>
  <p id="7gqP">Как только вы это сделаете, перед строкой приглашения вашей консоли появится “(venv)”, например у меня (было &gt; стало):</p>
  <pre id="fAj6">“[dmrlx@vws my_project]$” &gt; “(venv) [dmrlx@vws my_project]$”</pre>
  <p id="0GUM">Это значит, что контекст переключен и теперь мы используем виртуальное окружение! И это очень круто!</p>
  <p id="3yXX"><strong>4. &lt;Здесь мы работаем как обычно, не обращая особого внимания на то, что мы находимся в каком-то там виртуальном окружении&gt;</strong></p>
  <p id="xxps">Например, вам нужно установить некоторое количество пакетов, чтобы использовать в вашем фреймворке. Возьму что-нибудь из своих рабочих: paramiko, pycodestyle, pydocstyle, pylint, pytest, pytest-html, requests, SQLAlchemy. Устанавливаем (здесь “(venv) $” обращает внимание, что мы выполняем операцию с активированным виртуальным окружением):</p>
  <pre id="lEtL" data-lang="bash">(venv) $ pip install paramiko pycodestyle pydocstyle pylint pytest pytest-html requests SQLAlchemy</pre>
  <p id="sPbN">Когда процесс установки завершится, мы можем поглядеть список установленых пакетов:</p>
  <pre id="enZE" data-lang="bash">(venv) $ pip list</pre>
  <p id="OORm">Их будет больше, чем мы устанавливали, т.к. большинство из них подтянет свои зависимости + что-то устанавливается, когда вы только создаёте окружение.</p>
  <p id="xnV7">Теперь вы работаете, пишете код, устанавливаете в ваше виртуальное окружение ещё какие-то пакеты. Снова пишете. Снова устанавливаете…</p>
  <p id="5OL9"><strong>5. Перенос окружения</strong></p>
  <p id="ML3M">И вот настаёт счастливый день, когда вы хотите поделиться своим кодом с миром. Для этого нам нужно не только дать возможность скачать код из, например, github или gitlab, но и развернуть ТОЧНО ТАКОЕ ЖЕ окружение, какое было на вашей системе. Вот тут-то мы и начинаем понимать всю прелесть virtualenv’a. У нас уже есть это окружение, и нужно перенести его с вашей системы на внешнюю.</p>
  <p id="Dh9s">Для начала делаем “слепок” списка пакетов с их версиями:</p>
  <pre id="XrxY" data-lang="bash">(venv) $ pip freeze &gt; requirements.txt</pre>
  <p id="eHHw">где</p>
  <blockquote id="QfNF">pip freeze — команда, которая выводит список всех установленных пакетов с их версиями</blockquote>
  <blockquote id="g9rY">&gt; — символ перенаправления вывода</blockquote>
  <blockquote id="2PT7">requirements.txt — имя файла, куда мы складываем список пакетов из pip freeze; можно назвать иначе, но так принято )</blockquote>
  <p id="l7qy">Файл “requirements.txt” можно и нужно хранить в корне вашего проекта (прямо в директории my_project) и отдавать его в продакшн вместе с кодом. Это хороший тон. А вот класть в репозиторий папку с вашим виртуальным окружением — моветон и “так уже не носят”. Чтобы избежать этого, можете использовать файл .gitignore.</p>
  <p id="gVwm">Итак, вы скачали файл requirements.txt вместе с вашим репозиторием на новую машину, и теперь вам нужно развернуть точно такое же окружение, какое было у вас на рабочей системе. Для этого вы по очереди выполняете команды из этого мануала, чтобы создать виртуальное окружение:</p>
  <pre id="Y4Qt">$ sudo pip install virtualenv 
$ cd &lt;путь_к_репозиторию&gt; 
$ virtualenv venv --python=&lt;ваша_версия_python&gt; 
$ source venv/bin/activate</pre>
  <p id="kWrD">и теперь устанавливаете необходимые вам пакеты, используя requirements.txt:</p>
  <pre id="TNtJ">$ pip install -r requirements.txt</pre>
  <p id="8aPW">Вуаля! Когда процесс установки пакетов завершится, вы будете иметь на продкашн системе ровно такое же окружение, какое было у вас на рабочей системе, и тем самым сможете как минимум минимизировать (извините за тавтологию) проблемы с несовместимостью версий.</p>
  <p id="3Yub"><strong>6. Деактивация окружения</strong></p>
  <p id="Ssn1">Последний и самый простой этап работы с virtualenv. Чтобы покинуть контекст вашего виртуального окружения и вернуться в систему, вам нужно выполнить</p>
  <pre id="k2W2">$ deactivate</pre>
  <p id="G5PI">Да-да, так всё просто. Но старайтесь следить за тем, где вы находитесь (на какой машинке, в каком виртуальном окружении и т.д.), чтобы не сделать чего лишнего. Был случай, когда товарищ из, кажется, гитлаба перепутал окна консоли и удалил рабочую базу. С виртуальным окружением проблем такого масштаба у вас скорее всего не произойдёт, но лучше быть начеку.</p>
  <h2 id="rP58">🛬 Эпилог</h2>
  <p id="nYio">Virtualenv очень простой, но одновременно мощный и важный инструмент в арсенале любого питониста. Это то, что принято называть “must have”, и начинать им пользоваться лучше с самого начала. Учиться, так сказатб, хорошему. Это позволит избежать множества проблем при работе в команде, при работе на нескольких машинах (например, у вас дома есть ноутбук и десктоп, и вы разрабатываете на обоих), а так же сделает вашу жизнь чуточку удобнее, а волосы шелковистее.</p>
  <hr />
  <p id="ISk0">❓ Остались вопросы? Не стесняйтесь задавать их в комментариях. ;)</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/about</guid><link>https://teletype.in/@dmrlx/about?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/about?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>About</title><pubDate>Mon, 09 May 2022 08:16:38 GMT</pubDate><description><![CDATA[👋 Позвольте представиться. ]]></description><content:encoded><![CDATA[
  <p id="HshZ">👋 Позвольте представиться. </p>
  <p id="pyAv">🙃 Привет. Я Саша, мне глубоко за 30, сейчас временно живу в Тбилиси. Последние сколько-то там лет работаю Software Test Automation Engineer, пописываю на питончике, имею некоторое количество интересов, которые мне быстро приедаются и я их забрасываю. В этом бложике пытаюсь собирать свои заметки о разных штуках, которыми сам пользуюсь, и которые, как мне кажется, могут быть интересны кому-то ещё.</p>
  <p id="yOKU">✉️ Если вам вдруг по какой-то причине понадобилось связаться со мной, то можете использовать один из следующих способов:</p>
  <ul id="r3Ci">
    <li id="kGGh"><em>- telegram</em>: <a href="http://t.me/dmrlx" target="_blank">@dmrlx</a></li>
    <li id="OhDW"><em>- e-mail</em>: <a href="mailto:demuraalex@gmail.com" target="_blank">demuraalex@gmail.com</a></li>
  </ul>
  <p id="yhBY">Так же вы можете подписаться на меня в социальных сетях:</p>
  <ul id="hKfu">
    <li id="mEl6"><a href="https://twitter.com/dmrlx" target="_blank">🐥 Twitter</a></li>
    <li id="kQo7"><a href="https://www.instagram.com/dmrlx/" target="_blank">📷 Instagam</a></li>
  </ul>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/exp_2_no_mouse</guid><link>https://teletype.in/@dmrlx/exp_2_no_mouse?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/exp_2_no_mouse?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>🔬 Эксперимент #2: Отказ от манипулятора типа &quot;Мышь&quot;</title><pubDate>Tue, 21 Sep 2021 12:23:57 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/d3/6c/d36c654d-3f2b-49cd-b2f2-1705ed615341.jpeg"></media:content><category>эксперименты</category><description><![CDATA[<img src="https://img3.teletype.in/files/23/2c/232c7b19-3655-4f72-b4c4-e9e8ce7ff5f4.jpeg"></img>Да, #2. Я люблю что-то менять в жизни и скрипя зубами превозмогать. Экспериментом #1 был переход (успешный) с Chrome на Safari на всех устройствах. Благо у меня сейчас остались только iPhone, iPad и рабочий MacBook.]]></description><content:encoded><![CDATA[
  <figure id="58aB" class="m_column">
    <img src="https://img3.teletype.in/files/23/2c/232c7b19-3655-4f72-b4c4-e9e8ce7ff5f4.jpeg" width="1300" />
  </figure>
  <p id="4wmf">Да, #2. Я люблю что-то менять в жизни и скрипя зубами превозмогать. Экспериментом #1 был переход (успешный) с Chrome на Safari на всех устройствах. Благо у меня сейчас остались только iPhone, iPad и рабочий MacBook.</p>
  <p id="KBOK"><strong><u>Сегодня я отказываюсь от мышки и полностью перехожу на тачпад на месяц.</u></strong></p>
  <p id="uIci">Сразу немного предыстории. С 2001 года я провожу львиную долю своего времени за компьютером. В &quot;прошлой жизни&quot; я был инженером-конструктором, рисовал всякое там 3D и мышка всегда была лучшим другом и помощником. Так что переход вряд ли будет очень уж простым. Но нужно попробовать. Говорят, так тоже можно жить и это удобно. Тачпад в макбуке, кстати, просто топовейший, вам не врали.</p>
  <p id="1fY8">Из сложностей вижу необходимость быстро прыгать по большим файлам с кодом (нужно будет выучить комбинации клавиш в PyCharm) и 2 экрана по которым иногда нужно скакать (тут должны помочь горячие клавиши системы типа переключения между окнами приложений и приложение Rectangle).</p>
  <p id="FKEY">Штош, попробуем.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/pytest_commandline_params</guid><link>https://teletype.in/@dmrlx/pytest_commandline_params?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/pytest_commandline_params?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>⬆️ Передача параметров командной строки в PyTest</title><pubDate>Sun, 05 Sep 2021 10:59:00 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/34/f6/34f694f4-0180-4615-b21c-f61792cf02ab.png"></media:content><category>камплюхтеры</category><description><![CDATA[<img src="https://img4.teletype.in/files/ba/d6/bad6c75b-0cad-4188-93c8-e595c274f88a.jpeg"></img>И снова здарвствуйте! В это посте, который является продолжением Введения в Pytest я хотел бы рассказать как добавить гибкости вашим тестам с помощью параметров командной строки.]]></description><content:encoded><![CDATA[
  <figure id="KiSK" class="m_original">
    <img src="https://img4.teletype.in/files/ba/d6/bad6c75b-0cad-4188-93c8-e595c274f88a.jpeg" width="2400" />
  </figure>
  <p id="F1OH">И снова здарвствуйте! В это посте, который является продолжением <a href="https://teletype.in/@dmrlx/pytest_introduction" target="_blank">Введения в Pytest</a> я хотел бы рассказать как добавить гибкости вашим тестам с помощью параметров командной строки.</p>
  <p id="Eyhj">Допустим, мы хотим чтобы наш тест (из предыдущего поста) проверял на чётность не просто текущий таймстемп, а таймстемп плюс некоторое целое число, которое мы будем передавать в качестве параметра в тест. Чтобы это сделать, мы должны определить параметр командной строки, в который будет передаваться число. Для этого у pytest’а есть специальный метод “<a href="https://docs.pytest.org/en/latest/reference.html#initialization-hooks" target="_blank">pytest_addoption</a>”, который позволяет это сделать. Приводим conftest.py из предыдущего поста к такому виду:</p>
  <pre id="nM5p" data-lang="python">import time
import pytest

def pytest_addoption(parser):
    &quot;&quot;&quot;PyTest method for adding custom parameters.&quot;&quot;&quot;

    parser.addoption(&quot;--additional_value&quot;, 
                     action=&quot;store&quot;, 
                     default=0, 
                     type=int,
                     required=False,
                     help=&quot;Set additional value for timestamp&quot;)


@pytest.fixture(scope=&quot;session&quot;)
def additional_value(request):
    &quot;&quot;&quot;Handler for --additional_value parameter.&quot;&quot;&quot;

    return request.config.getoption(&quot;--additional_value&quot;)


@pytest.fixture(scope=&#x27;class&#x27;, autouse=True)
def suite_data():
    print(&quot;\n&gt; Suite setup&quot;)
    yield
    print(&quot;&gt; Suite teardown&quot;)

    
@pytest.fixture(scope=&#x27;function&#x27;)
def case_data():
    print(&quot;   &gt; Case setup&quot;)
    yield time.time()
    print(&quot;\n   &gt; Case teardown&quot;)</pre>
  <p id="0c4c">В функции <em>pytest_addoption()</em> мы объявляем необязательный (<em>required=False</em>) кастомный параметр типа integer (<em>type=int</em>) с дефолтным значением равным нулю (<em>default=0</em>), а функция <em>additional_value()</em> является, собственно, хэндлером значения. Каждый параметр, который вы добавите в <em>pytest_addoption()</em> должен иметь такой хэндлер, чтобы вы могли работать со значением параметра в своих тестах или других фикстурах. </p>
  <p id="092b">Теперь изменим наш тест-кейс из предыдущего поста таким образом, чтобы он зависел от передаваемого значения:</p>
  <pre id="eybM" data-lang="python">import pytest


class TestSuite():

    def test_case_1(self, case_data, additional_value):
        timestamp = int(case_data)
        parameter = additional_value
        print(f&quot;      &gt; Received from fixture timestamp is: {timestamp}&quot;)
        print(f&quot;      &gt; Received from command line parameter is: {parameter}&quot;)
        timestamp += parameter
        assert(timestamp % 2 == 0)</pre>
  <p id="r6Cm">Собственно, всё. Запускаем:</p>
  <figure id="zgdC" class="m_original">
    <img src="https://img1.teletype.in/files/c3/21/c3214d0a-2ef5-4c8b-a1c5-06e7c26f9007.png" width="2514" />
    <figcaption>Пример результата выполнения</figcaption>
  </figure>
  <p id="4b63">Мы видим, что timestamp у нас нечётный (1630838805), но благодаря переданному значению (9) в сумме он стал чётным и тест PASSED.</p>
  <p id="2c4e">Это очень синтетический пример, но, как мне кажется, достаточно понятный.</p>
  <p id="e735">Параметров вы можете передавать сколько угодно, в нашем фреймворке мы используем около десятка. Это достаточно гибкий инструмент, который обычно удобнее, чем конфиги и позволяет быстрее конфигурировать запуск ваших тестов.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/pytest_introduction</guid><link>https://teletype.in/@dmrlx/pytest_introduction?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/pytest_introduction?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>➡️ Введение в PyTest</title><pubDate>Sun, 05 Sep 2021 07:33:03 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/64/0a/640a4a43-3464-42c2-8fd6-1c9878fb8a54.jpeg"></media:content><category>камплюхтеры</category><description><![CDATA[<img src="https://img3.teletype.in/files/eb/4f/eb4f9ba4-eb4f-4e94-80a4-57ca096367e1.jpeg"></img>Я уже как-то пытался сделать небольшой цикл про PyTest на другой платформе, но застрял, кажется, на втором. Попробуем ещё разок.]]></description><content:encoded><![CDATA[
  <figure id="5SIg" class="m_original">
    <img src="https://img3.teletype.in/files/eb/4f/eb4f9ba4-eb4f-4e94-80a4-57ca096367e1.jpeg" width="7325" />
  </figure>
  <p id="t7TC">Я уже как-то пытался сделать небольшой цикл про PyTest на другой платформе, но застрял, кажется, на втором. Попробуем ещё разок.</p>
  <hr />
  <p id="8Fon">В проекте, на котором я сейчас работаю как Software Test AutomationEngineer в качестве основы для тестового фреймворка используется PyTest. Фреймворк отличный и очень мощный, но, на первый взгляд, может показаться достаточно сложным, и, с, опять же, на первый взгляд, отвратительной, совершенно непонятной <a href="https://docs.pytest.org" target="_blank">документацией</a>.</p>
  <p id="ed0b">Сначала расскажу в двух словах о структуре PyTest-проекта. Можно считать, что минимально он состоит из файла <em>conftest.py</em>, в котором по умолчанию хранятся т.н. &quot;фикстуры&quot; и, собственно, файла с тестами, например <em>test_intro.py</em>. Файлы <em>__init__.py </em>пустые и нужны в основном чтобы “поддерживать” структуру фреймворка.</p>
  <pre id="cqzs">pytest_intro
├── __init__.py
└── tests
    ├── __init__.py
    ├── conftest.py
    └── test_intro.py</pre>
  <p id="P8sV">Фикстуры — это функции, выполняемые pytest до (а иногда и после) фактических тестовых функций. Код в фикстуре может делать все, что вам необходимо. Вы можете использовать фикстуры, чтобы получить набор данных для тестирования. Вы можете использовать фикстуры, чтобы получить систему в известном состоянии перед запуском теста. Фикстуры также используются для получения данных для нескольких тестов.</p>
  <p id="biR9">Итак, рассмотрим наш <em>conftest.py</em></p>
  <pre id="main" data-lang="python">import time
import pytest


@pytest.fixture(scope=&#x27;class&#x27;, autouse=True)
def suite_data():
    print(&quot;\n&gt; Suite setup&quot;)
    yield
    print(&quot;&gt; Suite teardown&quot;)


@pytest.fixture(scope=&#x27;function&#x27;)
def case_data():
    print(&quot;   &gt; Case setup&quot;)
    yield time.time()
    print(&quot;\n   &gt; Case teardown&quot;)</pre>
  <p id="mF5q">Здесь мы видим две функции (suite_data() и case_data()), которые и являются фикстурами. Об этом нам говорит декоратор @pytest.fixture. У этого декоратора есть довольно много параметров, но главным является scope. Scope говорит об &quot;уровне&quot;, на котором будет выполнена фикстура. Всего их 4:</p>
  <ul id="KN8b">
    <li id="394U">&quot;session&quot; — означает, что данная фикстура будет выполняться для всей тестовой сессии;</li>
    <li id="KK57">&quot;module&quot; — означает, что данная фикстура будет выполняться для каждого тестового модуля (читай &quot;файла с тестами&quot;), который мы будем запускать;</li>
    <li id="W3LP">&quot;class&quot; — означает, что данная фикстура будет выполняться для каждого сьюта (читай &quot;класса&quot;), который мы будем запускать;</li>
    <li id="Ft7D">&quot;function&quot; — означает, что данная фикстура будет выполняться для каждого конкретного теста. Это значение является значением по умолчанию и его можно не указывать.</li>
  </ul>
  <p id="ZudU">В нашем примере, фикстура “suite_data()” будет выполнена для каждого тест сьюта. Внутри сьютов содержатся тест кейсы, для которых будет выполнена фикстура “case_data()” если она указана как входной параметр для кейса.</p>
  <p id="4KO5">Из фикстуры можно передать значение в сьют или кейс с помощью оператора yield. При этом после yield можно добавить ещё код, который будет выполнен после кейса. Таким образом можно сказать, что всё, что идёт до оператора yield является “setup” частью (подготовительной), а всё, что после — “teardown” (пост-часть). yield, к слову, может ничего и не возвращать (к в suite_data()), а просто быть разделителем, отделяющим сетап от тирдауна и это потрясающе удобно на практике.</p>
  <p id="JPAp">Например, мы тестируем какую-то систему и нам нужно перед каждым сьютом привести систему к какому-то состоянию, а после вернуть её в изначальное состояние. Мы в сетап-части собираем информацию о первоначальном состоянии системы, приводим её в нужное нам для тестов состояние, потом вставляем yield, выполняются кейсы и после их прогона начинается тирдаун, который возвращает систему в первоначальное состояние. Это же релевантно и для кейсов.</p>
  <p id="ALAu">Ну и напоследок напишем кейс, который будет забирать таймстемп из фикстуры case_data, печатать его и проверять чётность числа. Если число чётное результат будет PASSED, если нет — FAILED. Отступы с символами “&gt;” добавлены специально чтобы было лучше видно как “иерархически” выполняются фикстуры.</p>
  <p id="MrSd">Ниже приведено содержимое файла test_intro.py</p>
  <pre id="yroM" data-lang="python">import pytest


class TestSuite():

    def test_case_1(self, case_data):
        timestamp = int(case_data)
        print(f&quot;      &gt; Received from fixture timestamp is: {timestamp}&quot;)
        assert(timestamp % 2 == 0)</pre>
  <p id="Jf37">В тесте мы сначала приводим к целочисленному значению то, что нам возвращает фикстура case_data и присваиваем это значение переменной timestamp, затем печатаем строку, которая содержит этот timestamp для наглядности и в конце проверяем является ли timestamp чётным числом.</p>
  <p id="2J3r">Запускается это всё из консоли командой</p>
  <pre id="CFXR">pytest -v -s tests/testfile_name.py </pre>
  <p id="27u9">где -v — “verbose” режим, а -s — перенаправляет аутпут в консоль.</p>
  <p id="bi4x">Если захотите вызвать отдельный сьют или отдельный кейс из сьюта, то нужно через :: добавить в конце имя сьюта, а потом, если нужно, и имя кейса: </p>
  <pre id="PqT4">pytest -vs tests/testfile_name.py::TestSuiteName::test_case_name</pre>
  <figure id="22e5" class="m_original">
    <img src="https://img1.teletype.in/files/40/f7/40f78b7c-2304-447a-b3ad-15b0b6907fa3.png" width="2514" />
    <figcaption>Пример результата выполнения</figcaption>
  </figure>
  <p id="Q67r">В аутпуте хорошо видно в какой очерёдности выводятся строки из suite_data(), case_data() и нашего теста. Получается, что фикстуры как матрёшки &quot;оборачивают&quot; тест и друг друга. Сначала фикстура со скоупом function &quot;оборачивает&quot; тест кейс, потом фикстура со скоупом class &quot;оборачивает&quot; тест сьют и так далее до session.</p>
  <hr />
  <p id="IO2L">На этом, кажется, всё. В следующем посте я постараюсь рассказать про передачу параметров командной строки в pytest.</p>
  <p id="eDDk">Stay tuned, так сказать</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@dmrlx/sponsorblock</guid><link>https://teletype.in/@dmrlx/sponsorblock?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx</link><comments>https://teletype.in/@dmrlx/sponsorblock?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=dmrlx#comments</comments><dc:creator>dmrlx</dc:creator><title>🕸 Расширение для браузера SponsorBlock</title><pubDate>Sun, 29 Aug 2021 16:23:44 GMT</pubDate><description><![CDATA[За полтора года на удалёнке я привык глядеть видео на YouTube. Раньше это был какой-то очень странный способ получения информации, очень нерациональный с точки зрения затрат времени. А теперь я понял, что есть видосики, которые прикольно поглядеть или послушать фоном в процессе какой-то скучной спинномозговой работы.]]></description><content:encoded><![CDATA[
  <p>За полтора года на удалёнке я привык глядеть видео на YouTube. Раньше это был какой-то очень странный способ получения информации, очень нерациональный с точки зрения затрат времени. А теперь я понял, что есть видосики, которые прикольно поглядеть или послушать фоном в процессе какой-то скучной спинномозговой работы.</p>
  <p>Привык я, крч, настолько, что даже купил себе подписку YouTube Premium потому что реклама на YouTube ужасна. Но эти вот видеоблогеры придумали искать спонсоров. И это пиздец! Я плачу за то, чтобы не видеть рекламы, а они беззастенчиво суют её в видосы да ещё и не размечают. И вот посреди нормального видоса я вдруг слышу &quot;а чтобы отдохнуть от учёбы или работы вы можете купить себе PS5 с инновационными джойстиками и поиграть в ультрасовременные игры&quot;. Да вашу ж мать!!!</p>
  <p>Но мир не без добрых людей.</p>
  <p>Какой-то очень приятный человек, придумал расширение SponsorBlock для вашего браузера, которое позволяет вам отметить, что от сих до сих идёт споносрская реклама в видео и все остальные пользователи приложения смогут его автоматически или вручную пропустить! Идея просто топчик, три из трёх!</p>
  <p>Нет, меня не мучает совесть. Я плачу чтобы не видеть рекламы и сплю очень хорошо. Спасибо.</p>
  <p><a href="https://ilyabirman.ru/projects/typography-layout/faq/" target="_blank">Ссылка на github страницу расширения</a>, где вы сможете найти расширение для вашего браузера.</p>

]]></content:encoded></item></channel></rss>