python-reflexion/slides/index.html
2025-10-21 20:38:10 +02:00

237 lines
7.6 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Réflexivité avec Python: Introduction à importlib et inspect — PyConFr 2025</title>
<link rel="stylesheet" href="slides.css">
<link rel="stylesheet" href="highlight/default.min.css">
<script src="highlight/highlight.min.js"></script>
</head>
<body>
<section class="title">
<div class="content">
<h1>Réflexivité avec Python: Introduction à <code>importlib</code> et <code>inspect</code></h1>
</div>
</section>
<section>
<!-- Présentation -->
</section>
<section>
<!-- Problématique + démo -->
</section>
<section>
<h2>Définition</h2>
<div class="content">
<div>
<blockquote>
En programmation informatique, la réflexion est la capacité d'un programme à <b>examiner</b> et éventuellement à <b>modifier</b> ses propres structures internes de haut niveau lors de son exécution.
</blockquote>
<p>— Wikipédia</p>
</div>
</div>
</section>
<section>
<div class="content">
<h2>La gestion des imports en Python: <code>importlib</code></h2>
</div>
</section>
<section>
<h3>Le rôle d<code>importlib</code></h3>
<div class="content">
<ul>
<li>Implémente la logique dimport de Python: <code>__import__(name, **args)</code></li>
<li>Méchanisme en deux étapes:
<ul>
<li>trouver les modules (<code>abc.MetaPathFinder</code>, <code>abc.PathEntryFinder</code>),</li>
<li>charger les modules (<code>abc.Loader</code>),</li>
<li>des fois implémentées par la même classe (<code>BuiltinImporter</code>, <code>zipimporter</code>…).</li>
</ul>
</li>
</ul>
</div>
</section>
<section>
<h3>Liens avec le module <code>sys</code></h3>
<div class="content">
<ul>
<li><b><code>sys.modules</code></b>: dictionnaire des modules importés</li>
<li>
<b><code>sys.meta_path</code></b>: liste de <code>MetaPathFinder</code>
<ul>
<li><code>BuiltinImporter</code></li>
<li><code>FrozenImporter</code></li>
<li><code>PathFinder</code></li>
</ul>
</li>
<li>
<b><code>sys.path_hook</code></b>: liste de fonctions (ou <i>callables</i>) retournant un <code>PathEntryFinder</code>
<ul>
<li><code>zipimporter</code></li>
<li><code>path_hook_for_FileFinder</code></li>
</ul>
</li>
<li>
<b><code>sys.path</code></b>: liste de chemins utilisés par <code>PathFinder</code> pour tenter de trouver le <code>PathEntryFinder</code> qui importera le module
<pre><code class="language-python">for hook in sys.path_hook:
for path in sys.path:
if finder := hook(path):
return finder</code></pre>
</li>
</ul>
</div>
</section>
<section>
<h3>Exemple: configurer un nouvel <i>importer</i></h3>
<div class="content">
<pre><code class="language-python">import sys
import dns_importer
# DnsImporter implémente PathEntryFinder
# et hérite de importlib._bootstrap_external._LoaderBasics
# pour l'implémentation de Loader
sys.path_hooks.append(dns_importer.DnsImporter)
# Ajout d'un chemin qui sera utilisé par l'importer
sys.path.append('dns+pylib://_pylib.hannaeko.eu')
from test_pkg.test_module import hello</code></pre>
<pre><code>test_module.test_pkg._pylib TXT "def hello():\010 print(\"hello world\")\010"
__init__.test_pkg._pylib TXT ""</code></pre>
</div>
</section>
<section>
<div class="content">
<h2><code>import_module(name, package=None)</code></h2>
</div>
</section>
<section>
<h3>Utilisation</h3>
<div class="content">
<pre>
<code class="language-python-repl">>>> from importlib import import_module
>>>
>>> mon_module = import_module('dns.resolver')
>>> autre_module = import_module('..resolver', package='dns.name')
>>>
>>> mon_module is autre_module
True</code>
</pre>
</div>
</section>
<section>
<div class="content">
<pre><code class="language-python-repl">>>> from importlib import import_module
>>>
>>> dns_resolver = import_module('dns.resolver')
>>>
>>> msg = dns_resolver.resolve('pycon.fr', 'A')
>>> print(msg.response.answer)
[&lt;DNS pycon.fr. IN A RRset: [&lt;185.34.33.85&gt;]&gt;]</code></pre>
</div>
</section>
<section>
<div class="content">
<pre><code class="language-python-repl">>>> from importlib import import_module
>>>
>>> module = import_module('dns.resolver')
>>> function = getattr(module, 'resolve')
>>>
>>> msg = function('pycon.fr', 'A')
>>> print(msg.response.answer)
[&lt;DNS pycon.fr. IN A RRset: [&lt;185.34.33.85&gt;]&gt;]</code></pre>
</div>
</section>
<section>
<h3>Exemple: un système de plugins</h3>
<div class="content">
<pre><code class="language-yaml">plugins:
- name: extra_plugins:Greetings
config:
who: PyConFr</code></pre>
<pre><code class="language-python-repl">>>> import demo
>>> plugins = demo.load_plugins()
>>> demo.fire_hook(plugins)
Hello PyConFr</code></pre>
</div>
</section>
<section>
<div class="content">
<pre><code class="language-python"># extra_plugins.py
class Greetings(Plugin):
def __init__(self, who: str) -> None:
self.who = who
def hook(self) -> None:
print(f'Greetings {self.who}')</code></pre>
</div>
</section>
<section>
<div class="content">
<pre><code class="language-python">def load_plugins() -> Sequence[Plugin]:
# …
plugins = []
for plugin_def in config['plugins']:
plugin_name = plugin_def['name']
plugin_config = plugin_def['config']
module_name, class_name = plugin_name.split(':')
module = import_module(module_name)
plugin_class = getattr(module, class_name)
plugin = plugin_class(**plugin_config)
plugins.append(plugin)
return plugins</code></pre>
</div>
</section>
<script>hljs.highlightAll();</script>
<script>
let pages = [];
const goToSlide = (relativeIndex) => {
const pageHeight = window.innerHeight;
const position = window.pageYOffset;
const page = pages[Math.ceil(position / pageHeight) + relativeIndex];
if (page) {
page.scrollIntoView();
}
}
const onLoad = () => {
pages = [...document.querySelectorAll('section')];
}
const onKeyUp = (e) => {
if (e.key === 'ArrowRight' || e.key === ' ') {
goToSlide(1);
e.preventDefault();
return false;
} else if (e.key === 'ArrowLeft') {
goToSlide(-1);
e.preventDefault();
}
}
window.addEventListener('load', onLoad, false );
window.addEventListener('keydown', onKeyUp, false);
</script>
</body>
</html>