<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>danigm.net - python</title><link>https://danigm.net/</link><description></description><lastBuildDate>Fri, 14 Nov 2025 12:00:00 +0100</lastBuildDate><item><title>openSUSE: The new git workflow</title><link>https://danigm.net/git-workflow.html</link><description>&lt;p&gt;openSUSE is migrating the package source management from &lt;strong&gt;osc&lt;/strong&gt; to
&lt;strong&gt;git&lt;/strong&gt;. I will try to explain here what it is the "git workflow" in
openSUSE and give some practical information for contributors and
maintainers.&lt;/p&gt;
&lt;p class="img"&gt;
  &lt;img src="/pictures/opensuse-git.png" /&gt;
&lt;/p&gt;

&lt;p&gt;You can find some documentation in the &lt;a href="https://en.opensuse.org/openSUSE:Git_Packaging_Workflow"&gt;openSUSE wiki&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;openSUSE uses &lt;a href="https://openbuildservice.org/"&gt;Open Build Service&lt;/a&gt; to
store package sources (.spec files and patches). The source control is
similar to &lt;a href="https://subversion.apache.org/"&gt;subversion&lt;/a&gt;, so you can
&lt;em&gt;osc checkout&lt;/em&gt;, &lt;em&gt;osc commit&lt;/em&gt;, &lt;em&gt;osc log&lt;/em&gt;, etc. to work with any package
source.&lt;/p&gt;
&lt;p&gt;I don't know all the reasons to do the change, and different people
will find different reasons, to support the change or to be against
it. I will just write here what I consider a good reason to migrate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Almost everyone has moved to git now, so today,
  &lt;a href="https://git-scm.com"&gt;git&lt;/a&gt; is the default source code management
  (scm) tool, almost all the people knows about
  &lt;a href="https://github.com"&gt;github&lt;/a&gt;, so it's good to be "&lt;em&gt;standard&lt;/em&gt;".&lt;/li&gt;
&lt;li&gt;With the concept of "&lt;em&gt;cheap&lt;/em&gt;" branches in git, it &lt;strong&gt;could&lt;/strong&gt; be
  easier to maintain different versions of the same package, same
  repository, different branches, instead of actual forks.&lt;/li&gt;
&lt;li&gt;Decouple the package source management from build. OBS does a lot of
  things, moving source code and review process to
  &lt;a href="https://gitea.com"&gt;gitea&lt;/a&gt; could reduce the complexity (but requires
  some integration).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;From OBS to gitea&lt;/h2&gt;
&lt;p&gt;All the development happens in &lt;a href="https://build.opensuse.org"&gt;OBS&lt;/a&gt;, so the
&lt;strong&gt;classic&lt;/strong&gt; workflow happens entirely there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;developer: branch -&amp;gt; checkout -&amp;gt; modify -&amp;gt; commit -&amp;gt; submit request&lt;/li&gt;
&lt;li&gt;maintainer: review -&amp;gt; accept/decline&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And everything is integrated in OBS that does the corresponding build
of the sources, checks with services, forward, etc.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;new&lt;/strong&gt; workflow changes almost all the interaction from
&lt;a href="https://build.opensuse.org"&gt;OBS&lt;/a&gt; to &lt;a href="https://src.opensuse.org"&gt;gitea&lt;/a&gt;. OBS is still
a really important piece of software in the process, but in the new
workflow it's a build backend, so the user interaction will be with
the git repo in &lt;a href="https://src.opensuse.org"&gt;gitea&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The new &lt;strong&gt;git workflow&lt;/strong&gt; happens in &lt;a href="https://src.opensuse.org"&gt;gitea&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;developer: fork -&amp;gt; clone -&amp;gt; modify -&amp;gt; commit -&amp;gt; push -&amp;gt; pull request&lt;/li&gt;
&lt;li&gt;maintainer: review -&amp;gt; approve/decline&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you may notice, the "&lt;em&gt;default&lt;/em&gt;" workflow is very similar, the
difference is in the tools, the &lt;strong&gt;classic&lt;/strong&gt; workflow occurs in
&lt;a href="https://build.opensuse.org"&gt;build.opensuse.org&lt;/a&gt; and the &lt;strong&gt;new&lt;/strong&gt; workflow in
&lt;a href="https://src.opensuse.org"&gt;src.opensuse.org&lt;/a&gt;. And the tools to work with the
sources are also different, &lt;strong&gt;osc&lt;/strong&gt; in the first one and &lt;strong&gt;git&lt;/strong&gt; in
the new workflow.&lt;/p&gt;
&lt;p&gt;And with this new workflow, there are some bots in gitea that sends
the changes to OBS, so for any pull request created you will have the
build results and approval from the bot if everything is building.&lt;/p&gt;
&lt;h2&gt;How to modify a package (leap 16.0)&lt;/h2&gt;
&lt;p&gt;Notice that you should have a &lt;a href="https://idp-portal.suse.com/univention/self-service/#page=createaccount"&gt;openSUSE account&lt;/a&gt; to work with the
gitea instance, and don't forget to &lt;a href="https://src.opensuse.org/user/settings/keys"&gt;configure your ssh key&lt;/a&gt; to be
able to push using the gitea@src.opensuse.org remote.&lt;/p&gt;
&lt;p&gt;So as a contributor, this is the basic workflow to follow to update a
package in openSUSE:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look for the source package in the pool
   (&lt;a href="https://src.opensuse.org/pool"&gt;https://src.opensuse.org/pool&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fork it in your user space, click the top right button. Skip this
   step if you have forked before&lt;/li&gt;
&lt;li&gt;Clone your fork and work on that repo, create a new branch, modify
   the spec file, add new soures, etc. And make sure to update the
   .changes file accordingly, you can still use &lt;code&gt;osc vc&lt;/code&gt; to do that.&lt;/li&gt;
&lt;li&gt;You can build locally your package with &lt;code&gt;osc build&lt;/code&gt;, as usual, but
   you will need to specify the build project:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;obs&lt;span class="w"&gt; &lt;/span&gt;meta&lt;span class="w"&gt; &lt;/span&gt;pull
$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;obs&lt;span class="w"&gt; &lt;/span&gt;meta&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--project&lt;span class="w"&gt; &lt;/span&gt;openSUSE:Backports:SLE-16.0
$&lt;span class="w"&gt; &lt;/span&gt;osc&lt;span class="w"&gt; &lt;/span&gt;build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Once you are happy with your changes, you can commit, push to your
   fork and then you can create a Pull Request. The pull request
   should target the &lt;strong&gt;pool&lt;/strong&gt; repo and desired product branch. Right
   now you can just target &lt;code&gt;leap-16.0&lt;/code&gt;, as factory is not migrated
   yet.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Devel projects&lt;/h2&gt;
&lt;p&gt;Some devel projects have been migrated now to git, so a similar
workflow is available to modify these packages in Tumbleweed. The
development of these packages is not happening in the &lt;em&gt;pool&lt;/em&gt;, so you
need to find first the devel project.&lt;/p&gt;
&lt;p&gt;There's no simple way to discover the actual package devel source, as
far as I know the easier way is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look for the devel project of the package in the &lt;a href="https://src.opensuse.org/openSUSE/Factory/src/branch/main/pkgs/_meta/devel_packages"&gt;Factory list&lt;/a&gt;,
   (ex python-pytest)&lt;/li&gt;
&lt;li&gt;Go to the devel project in &lt;a href="1"&gt;OBS&lt;/a&gt; (ex &lt;a href="https://build.opensuse.org/project/show/devel:languages:python:pytest"&gt;devel:languages:python:pytest&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Click on the link &lt;a href="https://src.opensuse.org/python-pytest/_ObsPrj.git#main"&gt;This project is managed in SCM&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once you have located the desired package to modify, the workflow is
similar to what I explained before, but instead of forking from
&lt;strong&gt;pool&lt;/strong&gt;, you should fork from the devel project. For example if you
want to modify &lt;strong&gt;python-pytest&lt;/strong&gt; you should fork from
&lt;a href="https://src.opensuse.org/python-pytest/python-pytest"&gt;https://src.opensuse.org/python-pytest/python-pytest&lt;/a&gt;, 
and create the pull request for that repo, to the main branch.&lt;/p&gt;
&lt;p&gt;Adding a new package follows a different process that's documented in
the &lt;a href="https://en.opensuse.org/openSUSE:OBS_to_Git#How_to_create_a_new_package?"&gt;wiki&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;What's migrated?&lt;/h2&gt;
&lt;p&gt;As I said, the migration is happening right now, so not everything is
migrated. The first project migrated was the Leap 16.0. And we are
slowly migrating some devel projects.&lt;/p&gt;
&lt;p&gt;Right now from the python-maintainers team, we've started the
migration of two subprojects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://src.opensuse.org/python-interpreters"&gt;python-interpreters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://src.opensuse.org/python-pytest"&gt;python-pytest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find more information about the migration current state in the
wiki: &lt;a href="https://en.opensuse.org/openSUSE:OBS_to_Git#Codestream_Project_Status_table"&gt;https://en.opensuse.org/opensuse:obs_to_git#codestream_project_status_table&lt;/a&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Fri, 14 Nov 2025 12:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2025-11-14:/git-workflow.html</guid><category>blog</category><category>suse</category><category>opensuse</category><category>git</category><category>packaging</category><category>python</category></item><item><title>Python 3.13 Beta 1</title><link>https://danigm.net/python313-beta1.html</link><description>&lt;p&gt;&lt;img src="/pictures/python-logo-master-v313-TM.png" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;Python &lt;a href="https://www.python.org/downloads/release/python-3130b1/"&gt;3.13 beta 1 is out&lt;/a&gt;, and I've been working on the openSUSE
Tumbleweed package to get it ready for the release.&lt;/p&gt;
&lt;h2&gt;Installing python 3.13 beta 1 in Tumbleweed&lt;/h2&gt;
&lt;p&gt;If you are adventurous enough to want to test the python 3.13 and you
are using openSUSE Tumbleweed, you can give it a try and install the
current devel package:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# zypper addrepo -p 1000 https://download.opensuse.org/repositories/devel:languages:python:Factory/openSUSE_Tumbleweed/devel:languages:python:Factory.repo&lt;/span&gt;
&lt;span class="c1"&gt;# zypper refresh&lt;/span&gt;
&lt;span class="c1"&gt;# zypper install python313&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;What's new in Python 3.13&lt;/h2&gt;
&lt;p&gt;Python interpreter is pretty stable nowadays and it doesn't change too
much to keep code compatible between versions, so if you are writing
modern Python, your code should continue working whit this new
version. But it's actively developed and new versions have cool new
functionalities.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3.13/whatsnew/3.13.html#a-better-interactive-interpreter"&gt;New and improved interactive interpreter&lt;/a&gt;, colorized prompts,
   multiline editing with history preservation, interactive help with
   &lt;code&gt;F1&lt;/code&gt;, history browsing with &lt;code&gt;F2&lt;/code&gt;, paste mode with &lt;code&gt;F3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A set of performance improvements.&lt;/li&gt;
&lt;li&gt;Removal of many deprecated modules: aifc, audioop, chunk, cgi,
   cgitb, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev,
   pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib, lib2to3.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Enabling Experimental JIT Compiler&lt;/h2&gt;
&lt;p&gt;The python 3.13 version will arrive with an &lt;a href="https://docs.python.org/3.13/whatsnew/3.13.html#experimental-jit-compiler"&gt;experimental functionality&lt;/a&gt;
to improve performance. We're building with the
&lt;code&gt;--enable-experimental-jit=yes-off&lt;/code&gt; so it's disabled by default but it
can be enabled with a virtualenv before launching:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PYTHON_JIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python3.13
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Free-threaded CPython&lt;/h2&gt;
&lt;p&gt;The python 3.13 has another build option to disable the Global
Interpreter Lock (&lt;code&gt;--disable-gil&lt;/code&gt;), but we're not enabling it because
in this case it's not possible to keep the same behavior. Building
with &lt;code&gt;disabled-gil&lt;/code&gt; will break compatibility.&lt;/p&gt;
&lt;p&gt;In any case, maybe it's interesting to be able to provide another
version of the interpreter with the GIL disabled, for specific cases
where the performance is something critical, but that's something to
evaluate.&lt;/p&gt;
&lt;p&gt;We can think about having a &lt;code&gt;python313-nogil&lt;/code&gt; package, but it's not
something trivial to be able to have &lt;code&gt;python313&lt;/code&gt; and &lt;code&gt;python313-nogil&lt;/code&gt;
at the same time in the same system installation, so I'm not planning
to work on that for now.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Wed, 22 May 2024 00:00:00 +0200</pubDate><guid isPermaLink="false">tag:danigm.net,2024-05-22:/python313-beta1.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>linux</category><category>python</category></item><item><title>Where's my python code?</title><link>https://danigm.net/wheres-my-python-code.html</link><description>&lt;p&gt;&lt;img src="/pictures/python-logo-master-v3-TM.png" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;Python is a interpreted language, so the python code are just text
files with the &lt;code&gt;.py&lt;/code&gt; extension. For simple scripts it's really easy to
have your files located, but when you starts to use dependencies and
different projects with different requirements the thing starts to get
more complex.&lt;/p&gt;
&lt;h2&gt;PYTHONPATH&lt;/h2&gt;
&lt;p&gt;The Python interpreter uses a list of paths to try to locate python
modules, for example this is what you can get in a modern GNU/Linux
distribution by default:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Python&lt;/span&gt; &lt;span class="mf"&gt;3.11.7&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;GCC&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;linux&lt;/span&gt;
&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;copyright&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;credits&amp;quot;&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;license&amp;quot;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python311.zip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/lib-dynload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib64/python3.11/_import_failed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;/usr/lib/python3.11/site-packages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are the default paths where the python modules are installed. If
you install any python module using your linux packaging tool, the
python code will be placed inside the &lt;code&gt;site-packages&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;So system installed python modules can be located in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/usr/lib/python3.11/site-packages&lt;/code&gt; for modules that are
   architecture independent (pure python, all &lt;code&gt;.py&lt;/code&gt; files)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usr/lib64/python3.11/site-packages&lt;/code&gt; for modules that depends on
   the arquitecture, that's something that uses low level libraries
   and needs to build so there are some &lt;code&gt;.so&lt;/code&gt; files.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;pip&lt;/h2&gt;
&lt;p&gt;When you need a new python dependency you can try to install from your
GNU/Linux distribution using the default package manager like
&lt;code&gt;zypper&lt;/code&gt;, &lt;code&gt;dnf&lt;/code&gt; or &lt;code&gt;apt&lt;/code&gt;, and those python files will be placed in the
system paths that you can see above.&lt;/p&gt;
&lt;p&gt;But distributions doesn't pack all the python modules and even if they
do, you can require an specific version that's different from the one
packaged in your favourite distribution, so in python it's common to
install dependencies from the &lt;a href="https://pypi.org/"&gt;Python Package Index (PyPI)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Python has a tool to install and manage Python packages that looks for
desired python modules in PyPI.&lt;/p&gt;
&lt;p&gt;You can install new dependencies with &lt;code&gt;pip&lt;/code&gt; just like:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;django
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And that command looks for the &lt;code&gt;django&lt;/code&gt; python module in the PyPI,
downloads and install it, in your user
&lt;code&gt;$HOME/.local/lib/python3.11/site-packages&lt;/code&gt; folder if you
use &lt;code&gt;--user&lt;/code&gt;, or in a global system path like &lt;code&gt;/usr/local/lib&lt;/code&gt; or
&lt;code&gt;/usr/lib&lt;/code&gt; if you run pip as root.&lt;/p&gt;
&lt;p&gt;But the usage of &lt;code&gt;pip&lt;/code&gt; directly in the system is something &lt;strong&gt;not
recommended today&lt;/strong&gt;, and even &lt;a href="https://packaging.python.org/en/latest/specifications/externally-managed-environments/#externally-managed-environments"&gt;it's disabled&lt;/a&gt; in some
distributions, like openSUSE Tumbleweed.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;danigm&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;django&lt;/span&gt;
&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;externally&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;managed&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;

&lt;span class="err"&gt;×&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;externally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;managed&lt;/span&gt;
&lt;span class="err"&gt;╰─&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;To&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;wide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;try&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;zypper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python311&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trying&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rpm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packaged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python3&lt;/span&gt;&lt;span class="m m-Double"&gt;.11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;Then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;wish&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;non&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;rpm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packaged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;may&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;easiest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;xyz&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;virtual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;via&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nx"&gt;zypper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python311&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;pipx&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;believe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mistake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;please&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;distribution&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;You&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;risk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;breaking&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;passing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nx"&gt;hint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;See&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PEP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;668&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;detailed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;specification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;virtualenvs&lt;/h2&gt;
&lt;p&gt;Following the current recommendation, the correct way of installing
third party python modules is to use &lt;code&gt;virtualenvs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;virtualenvs&lt;/code&gt; are just specific folders where you install your
python modules and some scripts that make's easy to use it in
combination with your system libraries so you don't need to modify the
&lt;code&gt;PYTHONPATH&lt;/code&gt; manually.&lt;/p&gt;
&lt;p&gt;So if you've a custom project and want to install python modules you
can create your own virtualenv and use pip to install dependencies
there:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm@localhost tmp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;
&lt;span class="n"&gt;Collecting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Successfully&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;asgiref&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;3.7.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;5.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sqlparse&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.4.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So all dependencies are installed in my new virtualenv folder and if I
use the python from the virtualenv it's using those paths, so all the
modules installed there are usable inside that virtualenv:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.11&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;apps&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;contrib&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;forms&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;shortcuts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;templatetags&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;views&lt;/span&gt;
&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;__pycache__&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;utils&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;import django; print(django.__version__)&amp;quot;&lt;/span&gt;
&lt;span class="mf"&gt;5.0.1&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myenv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;danigm&lt;/span&gt;&lt;span class="nd"&gt;@localhost&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deactivate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With virtualenvs you can have multiple python projects, with different
dependencies, isolated, so you use different dependencies when you
activate your desired virtualenv:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;activate &lt;code&gt;$ . ./myenv/bin/activate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;deactivate &lt;code&gt;$ deactivate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;High level tools to handle virtualenvs&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://docs.python.org/3/library/venv.html#module-venv"&gt;venv&lt;/a&gt; module is a default Python module and as you can see
above, it's really simple to use, but there are some tools that
provides some tooling around it, to make it easy for you, so usually
you don't need to use &lt;code&gt;venv&lt;/code&gt; directly.&lt;/p&gt;
&lt;h3&gt;pipx&lt;/h3&gt;
&lt;p&gt;For final python tools, that you are not going to use as dependencies
in your python code, the recommended tool to use is &lt;a href="https://github.com/pypa/pipx"&gt;pipx&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/pictures/pipx_demo.gif" width="100%" /&gt;&lt;/p&gt;
&lt;p&gt;The tool creates virtualenv automatically and links the binaries so
you don't need to worry about anything, just use as a way to install
third party python applications and update/uninstall using it. The
&lt;code&gt;pipx&lt;/code&gt; won't mess your system libraries and each installation will use
a different virtualenv, so even tools with incompatible dependencies
will work nicely together in the same system.&lt;/p&gt;
&lt;h3&gt;Libraries, for Python developers&lt;/h3&gt;
&lt;p&gt;In the case of Python developers, when you need to manage dependencies
for your project, there are a lot of nice high level tools for
&lt;a href="https://packaging.python.org/en/latest/tutorials/managing-dependencies/#other-tools-for-application-dependency-management"&gt;managing dependencies&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pdm-project/pdm"&gt;PDM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pypa/hatch"&gt;hatch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/thoth-station/micropipenv"&gt;micropipenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jazzband/pip-tools"&gt;pip-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://packaging.python.org/en/latest/key_projects/#pipenv"&gt;pipenv&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/python-poetry/poetry"&gt;poetry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tools provides different ways of managing dependencies, but all
of them relies in the use of &lt;code&gt;venv&lt;/code&gt;, creating the virtualenv in
different locations and providing tools to enable/disable and manage
dependencies inside those virtualenvs.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;poetry&lt;/code&gt; creates virtualenvs by default inside the
&lt;code&gt;.cache&lt;/code&gt; folder, in my case I can find all poetry created virtualenvs
in:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;/home/danigm/.cache/pypoetry/virtualenvs/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Most of these tools add other utilities on top of the dependency
management. Just for installing python modules easily you can always
use default &lt;code&gt;venv&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; modules, but for more complex projects
it's worth to investigate high level tools, because it'll make easy to
manage your project dependencies and virtualenvs.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;There are a lot of python code inside any modern Linux distribution
and if you're a python developer it's possible to have a lot of python
code. Make sure to know the source of your modules and do not mix
different environments to avoid future headaches.&lt;/p&gt;
&lt;p&gt;As a final trick, if you don't know where's the actual code of some
python module in your running python script, you can always ask:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;django&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;/tmp/myenv/lib64/python3.11/site-packages/django/__init__.py&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This could be even more complicated if you start to use containers
and different python versions, so keep you dependencies clean and up
to date and make sue that you know &lt;strong&gt;where is your Python code&lt;/strong&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 03 Feb 2024 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2024-02-03:/wheres-my-python-code.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>linux</category><category>python</category></item><item><title>Hackweek 2023</title><link>https://danigm.net/hackweek22.html</link><description>&lt;p&gt;&lt;center&gt;
    &lt;img src="/pictures/hackweek.png" width="50%"/&gt;
&lt;/center&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hackweek.opensuse.org/"&gt;Hack Week&lt;/a&gt; is the time SUSE employees experiment, innovate &amp;amp; learn
interruption-free for a whole week! Across teams or alone, but always without
limits.&lt;/p&gt;
&lt;p&gt;This year the Hack Week was this week, the last week of January and for my
first SUSE hack week I decided to work in something funny, &lt;a href="https://hackweek.opensuse.org/22/projects/linux-immersive-learning-system-lils"&gt;LILS&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Linux Immersive Learning System (LILS)&lt;/h2&gt;
&lt;p&gt;I don't think that this is a good name, but don't focus on it. The main idea of
this project is to create some basic machinery to be able to write
"interactive" tutorials or games using the &lt;a href="https://www.inklestudios.com/ink/"&gt;INK language&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is not an original idea, indeed all I've done is something that's
currently working on &lt;a href="https://www.endlessos.org/"&gt;EndlessOS&lt;/a&gt;, and was the main idea behind the dead
project &lt;a href="https://www.hack-computer.com/"&gt;Hack Computer&lt;/a&gt;, you can even take a look to the
&lt;a href="https://flathub.org/apps/details/com.hack_computer.Clubhouse"&gt;Hack app in flathub&lt;/a&gt;. But I wanted to work around this, and create
something simpler, from scratch.&lt;/p&gt;
&lt;p&gt;I wanted to build something simple, with just Python, and make it simple enough
to be able to build other tools on top. The design is simple, an INK parser,
with a simple game runner. In the INK script you can define &lt;em&gt;commands&lt;/em&gt;, to do
something special, and wait for events with &lt;em&gt;listeners&lt;/em&gt;, to wait for an event
in the OS to continue.&lt;/p&gt;
&lt;p&gt;With this basic functionality it's possible to build different user interfaces
for different environments. And the original idea was to make the &lt;em&gt;commands&lt;/em&gt;
and &lt;em&gt;listeners&lt;/em&gt; something extensible with a simple API, but that's something
that I have not done yet, it's all Python functions without extension point.&lt;/p&gt;
&lt;p&gt;The code can be found in &lt;a href="https://github.com/danigm/lils"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The INK parser&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;img src="/pictures/inky.png" /&gt;
&lt;/p&gt;

&lt;p&gt;The most complex part of this project is the INK language parser. The &lt;a href="https://github.com/inkle/ink"&gt;Ink&lt;/a&gt;
parser is free software and there's a Linux version that you can use to parse
and &lt;strong&gt;compile&lt;/strong&gt; to &lt;em&gt;json&lt;/em&gt;, but I wanted to create my own parser with Python.&lt;/p&gt;
&lt;p&gt;I've spent most of the Hack Week time fighting with the parser and indeed was
the most challenging and fun part, because I've not worked a lot with parsers
and it's not something easy as pie 😛️.&lt;/p&gt;
&lt;p&gt;I remember creating a java compiler long time ago, when I was in the Seville
University, for the Language Processors course. We did that with &lt;a href="https://www.antlr.org/"&gt;ANTLR&lt;/a&gt;, so
starting from that, and looking for a Python lib, I found the &lt;a href="https://github.com/lark-parser/lark"&gt;Lark&lt;/a&gt;
project. So if you like regular expressions, writing a grammar is a lot more
FUN.&lt;/p&gt;
&lt;p&gt;At the end I was able to support some basic INK language with support for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Text&lt;/li&gt;
&lt;li&gt;Tag support&lt;/li&gt;
&lt;li&gt;Options, with suppress text support&lt;/li&gt;
&lt;li&gt;Knots, Stitches and Diverts&lt;/li&gt;
&lt;li&gt;Include other .ink files&lt;/li&gt;
&lt;li&gt;Variable definition and basic operations&lt;/li&gt;
&lt;li&gt;Knots and Stitches automatic visiting count variables&lt;/li&gt;
&lt;li&gt;Conditional options using variables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It still fails in some cases, the comments and &lt;em&gt;TODO&lt;/em&gt; placed in between text is
not detected correctly and there's a lot of complex stuff that's not supported
yet, but with what's supported right now it's possible to create complex
scripts with loops and complex game graphs, so it's good enough to build games
just with it.&lt;/p&gt;
&lt;h2&gt;GNOME shell extension&lt;/h2&gt;
&lt;p class="img"&gt;
    &lt;video src="/pictures/lils.mp4" width="100%" autoplay controls loop /&gt;
&lt;/p&gt;

&lt;p&gt;To integrate with the system I've done a simple &lt;a href="https://github.com/danigm/lils/tree/master/lils%40danigm.net"&gt;GNOME shell extension&lt;/a&gt;.
The extension just shows the text as bubbles and options as buttons, it's
really simple and I've no time to make it something ready to be used, but
I was able to make something usable.&lt;/p&gt;
&lt;p&gt;To be able to run the &lt;em&gt;LILS&lt;/em&gt; python library from &lt;em&gt;gjs&lt;/em&gt; I've created a simple
&lt;em&gt;dbus&lt;/em&gt; service that exposes the basic &lt;em&gt;InkScript&lt;/em&gt; class functionality as a dbus
API.&lt;/p&gt;
&lt;p&gt;I was thinking about being able to change the desktop background, depending of
the value of a &lt;em&gt;background&lt;/em&gt; variable in the script and do something similar to
play music and sounds, so it could be a cool &lt;em&gt;game engine&lt;/em&gt; with some additions.&lt;/p&gt;
&lt;h2&gt;SUSE Hack Week&lt;/h2&gt;
&lt;p&gt;So this Hack week was really fun and I learned a lot. It's really great that
SUSE does things like this, letting us work in different projects for a week,
to learn, to grow or to just explore different paths.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/IxscORgeqOY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Tue, 03 Jan 2023 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2023-01-03:/hackweek22.html</guid><category>blog</category><category>gnome</category><category>work</category><category>suse</category><category>ink</category><category>python</category></item><item><title>Timetrack app for GNOME</title><link>https://danigm.net/timetrack.html</link><description>&lt;p&gt;This week I started a new small project called &lt;a href="https://flathub.org/apps/details/net.danigm.timetrack"&gt;Timetrack&lt;/a&gt;, you can find
the code in the &lt;a href="https://gitlab.gnome.org/danigm/timetrack"&gt;gnome gitlab&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/timetrack1.png"&gt;
        &lt;img src="/pictures/timetrack1.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;This is a simple app to store an activity log in a sqlite database and to
provide useful reports in the future. The main idea is to use this for may day
to day work, so I'll be able to keep track of my working hours and other
activities.&lt;/p&gt;
&lt;h3&gt;The need&lt;/h3&gt;
&lt;p&gt;There's a lot of timetrack applications. There are good web apps and also some
desktop applications. At first, I tried to find a gnome-shell plugin, but I
didn't find any extension that is good for me.&lt;/p&gt;
&lt;p&gt;I've had a &lt;a href="https://github.com/wadobo/django-objs"&gt;custom simple web app&lt;/a&gt; to track my time.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/wadobo-objs.png"&gt;
        &lt;img src="/pictures/wadobo-objs.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I've been using this app for a long time, but I don't like to use web apps for
simple tasks so I want something local.&lt;/p&gt;
&lt;p&gt;So I tried with some gtk desktop applications, but none of the applications
that I've found looks good to me.&lt;/p&gt;
&lt;p&gt;I wanted a simple timetrack app, without any complex stuff, so I thought that it
won't be hard to implement it. So I started with a simple python app based
on the code of &lt;a href="https://gitlab.gnome.org/World/PasswordSafe/"&gt;Password Safe&lt;/a&gt;.&lt;/p&gt;
&lt;p class="img"&gt;
    &lt;a href="/pictures/timetrack2.png"&gt;
        &lt;img src="/pictures/timetrack2.png" /&gt;
    &lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;So I started to code and in two days I had a simple application that &lt;em&gt;works&lt;/em&gt;.
The applications is now on flathub so it's available for use.&lt;/p&gt;
&lt;h3&gt;The functionality&lt;/h3&gt;
&lt;p&gt;Right now, Timetrack has a limited set of features, but I think I'll improve
the app with new features as soon as I need something. So, the current version
can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track activity time with a simple button, showing the activity time spent&lt;/li&gt;
&lt;li&gt;List last activities&lt;/li&gt;
&lt;li&gt;Edit / Delete activities&lt;/li&gt;
&lt;li&gt;Simple report by day, week or month&lt;/li&gt;
&lt;li&gt;Report navigation using a calendar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've plans to add more functionalities in the near future, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Report export to plain text and csv&lt;/li&gt;
&lt;li&gt;Comments for activities&lt;/li&gt;
&lt;li&gt;Tags for activities&lt;/li&gt;
&lt;li&gt;Detailed reports&lt;/li&gt;
&lt;li&gt;Activity Graphs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this is stored in a sqlite database, if you install the app using flatpak
you can find the database in this path:
&lt;code&gt;~/.var/app/net.danigm.timetrack/data/timetrack.sqlite3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I've &lt;em&gt;done&lt;/em&gt; the icon for this app using the gnome-clocks icon and adding some
colors to it.&lt;/p&gt;
&lt;p&gt;If you find this app useful, don't hesitate and use it. Any feature request or
bug report is welcome. And of course, all is free software, so if you want to
collaborate, go ahead and send me some Merge Request.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Sat, 26 Jan 2019 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2019-01-26:/timetrack.html</guid><category>blog</category><category>gnome</category><category>timetrack</category><category>python</category></item><item><title>Monkey Patching</title><link>https://danigm.net/monkey-patching.html</link><description>&lt;h2&gt;Qué es el Monkey Patching&lt;/h2&gt;
&lt;p&gt;El &lt;a href="https://en.wikipedia.org/wiki/Monkey_patch"&gt;Monkey Patching&lt;/a&gt; es una técnica de programación de los lenguajes
dinámicos que consiste en modificar el código en tiempo de ejecución.&lt;/p&gt;
&lt;p&gt;Esto quiere decir que en lugar de modificar el código fuente de una clase,
por ejemplo, en tiempo de ejecución asignamos otra función a ese método y
todas las llamadas posteriores en lugar de ejecutar el código definido en
la clase ejecutarán el código "parcheado".&lt;/p&gt;
&lt;p&gt;Esto está relacionado con el llamado &lt;a href="https://es.wikipedia.org/wiki/Duck_typing"&gt;Duck typing&lt;/a&gt;, que viene a decir
que si nada como un pato y suena como un pato, esto es un pato. Y esto del
pato hace referencia a que los tipos no tienen que cumplir una interfaz
estricta, simplemente, si se comportan como tal, es el tipo esperado. Y con
el Monkey Patching podemos hacer que un perro haga cuak como un pato,
modificando el objeto perro en tiempo de ejecución.&lt;/p&gt;
&lt;blockquote&gt;
If it walks like a duck and talks like a duck, it’s a duck, right? So if
this duck is not giving you the noise that you want, you’ve got to just
punch that duck until it returns what you expect.
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;-- Patrick Ewing&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Qué hay que tener en cuenta para el Monkey Patching&lt;/h2&gt;
&lt;p&gt;Para poder hacer Monkey Patching necesitamos, &lt;strong&gt;tipado dinámico&lt;/strong&gt;, ya que si
un método espera un objeto tipo pato y le pasamos uno tipo perro, pero
"parcheado", el método no debe quejarse.&lt;/p&gt;
&lt;p&gt;También tenemos que tener en cuenta la &lt;strong&gt;vida útil&lt;/strong&gt; de los módulos y
objetos. Si parcheamos un módulo, es necesario conocer el alcance de esas
modificaciones, si sólo afectarán a mi módulo o si pueden afectar a otros
módulos que lo importen, o si parcheamos una instancia, es necesario
conocer hasta dónde llegarán esas modificaciones.&lt;/p&gt;
&lt;p&gt;Y por supuesto también es necesario tener acceso a las partes del código
que se quieran modificar, si vamos a modificar un objeto con métodos
&lt;strong&gt;privados&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Por &lt;em&gt;suerte&lt;/em&gt;, en Python no tenemos métodos privados realmente, se suele
usar el guión bajo (_metodo), que según la herramienta lo oculta como
privado, pero en realidad en Python todo es accesible y por tanto podemos
parchear lo que queramos.&lt;/p&gt;
&lt;h2&gt;Algunos ejemplos&lt;/h2&gt;
&lt;h3&gt;Veamos el ejemplo que viene en la wikipedia:&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;
&lt;span class="mf"&gt;3.141592653589793&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;En este ejemplo se ve que se importa el módulo &lt;em&gt;math&lt;/em&gt; y se modifica el valor
de &lt;em&gt;math.pi&lt;/em&gt;. Fácil y sencillo. No tiene mucho sentido este cambio, pero si
lo hacemos en nuestro código, ya todo el código que venga después y use
&lt;em&gt;math.pi&lt;/em&gt; recibirá como valor 3, y esto afecta incluso a librerías de
terceros.&lt;/p&gt;
&lt;h3&gt;Algo más práctico con código Django:&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shortcuts&lt;/span&gt;

&lt;span class="n"&gt;old_render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shortcuts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;custom_render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;old_render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Render time: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt; seconds&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;

&lt;span class="n"&gt;shortcuts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;custom_render&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;En este ejemplo modificamos el comportamiento por defecto de la función
render de django. Reemplazamos esta función por otra que lo único que hace
es medir el tiempo que se tarda en la llamada a la función inicial y
sacarlo por pantalla.&lt;/p&gt;
&lt;h3&gt;Otro ejemplo con Django:&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;password_logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;send_to_hacker_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;:&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;newp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real_set_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;admin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;real_set_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_password&lt;/span&gt;
&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password_logger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Aquí se muestra cómo se puede modificar una instancia. En este ejemplo se
modifica el método set_password del usuario admin, para que cuando se
llame a este método se envíe la nueva contraseña por email a un presunto
hacker.&lt;/p&gt;
&lt;p&gt;En el caso de los modelos de django no tiene mucho sentido modificar la
instancia, mejor sería modificar el modelo, la definición de la clase, pero
vale como ejemplo.&lt;/p&gt;
&lt;h2&gt;Cuándo usar el Monkey Patching&lt;/h2&gt;
&lt;p&gt;El Monkey Patching es una herramienta muy poderosa, pero a la vez, es algo
tremendamente peligroso, ya que hace que una clase o un módulo no se
ejecuten como se espera, como dice su definición y por tanto puede dar más
de un dolor de cabeza.&lt;/p&gt;
&lt;blockquote&gt;
Un gran poder conlleva una gran responsabilidad
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;-- Ben Parker&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Por esto hay que saber cuándo usar el Monkey Patching y cuando no.&lt;/p&gt;
&lt;h3&gt;Cuando SÍ:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Depuración o ejecución paso a paso&lt;/strong&gt;: pdb, ipdb, mientras depuramos un
   código para que nos saque información por pantalla, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt;: Para reemplazar métodos, atributos o funciones en tiempo de
   ejecución en los tests, por ejemplo una función que genera números
   aleatorios la podemos transformar en algo predecible para testear otros
   métodos relacionados, o podemos reemplazar funciones que tarden mucho
   tiempo debido a sistemas externos, para que los tests se puedan ejecutar
   rápidamente.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fixs o arreglos de libs externas&lt;/strong&gt;: Para aplicar un parche o un
   arreglo de una biblioteca externa en una versión que aún no ha sido
   parcheada oficialmente.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cuando NO:&lt;/h3&gt;
&lt;p&gt;En la mayoría de las ocasiones, no es recomendable usar Monkey Patching.
Por lo tanto, si hay otra forma de hacer lo que queremos hacer, seguramente
sea mejor idea que el Monkey Patching.&lt;/p&gt;
&lt;p&gt;Como he comentado antes, el Monkey Patching hace que el código que se
ejecuta sea diferente del descrito en la definición de la clase o módulo,
por tanto, hay que evitarlo y cuando se use hay que documentarlo muy bien,
porque si no, puede enmascarar problemas o hacer que futuras modificaciones
al código original no tengan efecto.&lt;/p&gt;
&lt;p&gt;Resumiendo, el Monkey Patching está muy guay, pero úsalo bajo tu propia
responsabilidad.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">danigm</dc:creator><pubDate>Thu, 02 Feb 2017 00:00:00 +0100</pubDate><guid isPermaLink="false">tag:danigm.net,2017-02-02:/monkey-patching.html</guid><category>blog</category><category>wadobo</category><category>python</category><category>programming</category><category>monkey patching</category></item></channel></rss>