We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Code reloading techniques in Python

00:00

Formale Metadaten

Titel
Code reloading techniques in Python
Untertitel
Cold and hot code reloading, the different options, how they work and when to use them.
Serientitel
Anzahl der Teile
542
Autor
Mitwirkende
Lizenz
CC-Namensnennung 2.0 Belgien:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
While iterating rapidly on Python code, we want to see the result of our changes rapidly. In this talk, we will review the different techniques available to reload Python code. We will see how they work and when each is the best fit. Please note that this talk replaces one titled "The future of Python's asyncio" that was due to have been given by Jonathan Slenders, who has sent his apologies but is now unable to attend as he has fallen ill. We wish him a speedy recovery. The talk will cover both cold and hot reload techniques: Cold reload techniques reset the application state between each reload. Examples include Django and Flask's autoreload tools. Hot reload techniques keep the application state despite the code changing. These include Jupyter kernels and 'reloadr' [1], an open-source module developed by the speaker to allow stateful hot code reloading. AMENDMENT: Please note that this talk replaces one titled "The future of Python's asyncio" that was due to have been given by Jonathan Slenders, who has sent his apologies but is now unable to attend as he has fallen ill. We wish him a speedy recovery.
14
15
43
87
Vorschaubild
26:29
146
Vorschaubild
18:05
199
207
Vorschaubild
22:17
264
278
Vorschaubild
30:52
293
Vorschaubild
15:53
341
Vorschaubild
31:01
354
359
410
MaschinenspracheComputeranimation
Software EngineeringEreignishorizontSocketLambda-KalkülZeichenketteVektorpotenzialSocket-SchnittstelleZeitzoneFramework <Informatik>SkriptspracheElektronische PublikationWeb-SeiteProzess <Informatik>VersionsverwaltungTermQuellcodePatch <Software>MaschinensprachePatch <Software>Elektronische PublikationProzess <Informatik>AggregatzustandEinsProgrammierumgebungMathematikVariableMereologieFramework <Informatik>InternetworkingKategorie <Mathematik>QuellcodeMaschinencodeBenutzerbeteiligungTermInterpretiererProgrammierungSocketComputeranimation
FreewareProzess <Informatik>AchtMaschinenspracheStörungstheorieGamecontrollerGanze FunktionFramework <Informatik>DatenbankBenutzerbeteiligungAggregatzustandComputeranimation
ThreadMechanismus-Design-TheoriePhysikalisches SystemEreignishorizontSystemprogrammierungElektronische PublikationVerzeichnisdienstp-BlockEin-AusgabeLuenberger-BeobachterHochdruckProzess <Informatik>ROM <Informatik>IRIS-TMaschinenspracheProzess <Informatik>Kartesische KoordinatenProgrammierungBenutzerbeteiligungBitThreadEreignishorizontElektronische PublikationDifferenzenrechnungNabel <Mathematik>StörungstheorieServerEinfach zusammenhängender RaumAggregatzustandMereologieObjekt <Kategorie>MathematikDateiverwaltungPhysikalisches SystemSocketDatenverwaltungMomentenproblemPortabilitätQuellcodeInterface <Schaltung>ComputerspielProgrammbibliothekComputeranimation
MaschinencodeDebuggingAppletProtokoll <Datenverarbeitungssystem>VersionsverwaltungProgrammbibliothekBimodulHochdruckLesen <Datenverarbeitung>Proxy ServerFunktion <Mathematik>QuellcodeMinkowski-MetrikAttributierte GrammatikProgrammierumgebungSummierbarkeitInstantiierungKlasse <Mathematik>Objekt <Kategorie>MaschinenspracheSpeicherbereinigungHalbleiterspeicherProxy ServerRegulärer GraphZählenEin-AusgabeComputersicherheitGamecontrollerArithmetischer AusdruckTypentheorieVariableMultiplikationsoperatorParametersystemBimodulFunktionalResultanteQuellcodeStörungstheorieSystemaufrufDebuggingVersionsverwaltungZeichenketteSoftwareschwachstelleAppletNamensraumPatch <Software>ProgrammbibliothekMaschinencodeElektronische PublikationDatenfeldDemo <Programm>SkriptspracheEinfach zusammenhängender RaumKategorie <Mathematik>ZahlenbereichLeistung <Physik>Exogene VariableByte-CodeAggregatzustandVererbungshierarchieInterface <Schaltung>DatenbankMereologieZweiNebenbedingungFormale SpracheComputeranimation
DatensichtgerätSpieltheorieFlächeninhaltW3C-StandardOvalE-MailGammafunktionFunktionalProgrammierungAggregatzustandVorzeichen <Mathematik>MultiplikationsoperatorMaschinensprache
MaschinencodeAggregatzustandStörungstheorieURLDatenbankMaschinenspracheRechenschieberBenutzerbeteiligungComputeranimation
Computeranimation
GammafunktionMIDI <Musikelektronik>KreisringW3C-StandardOvalE-MailLeistungsbewertungMaschinenspracheBefehlscodeNabel <Mathematik>ProgrammbibliothekInstallation <Informatik>
Flussdiagramm
Transkript: Englisch(automatisch erzeugt)
Please welcome Hugo, who's gonna do the next talk on code reloading techniques in Python. Hello everybody, and I'm glad to speak to you, and I hope I will have a good presentation,
you will be happy about replacing Jonathan's talk, who couldn't make it today. So we'll be talking about hot code reloading in Python, and a few different methods to do this. Short introduction, so I discovered Python in Linux in 2003, and I think I've attended
almost every FOSDEM since 2004, and I really, I think FOSDEM made me, and I'm really happy to be speaking here, to be here with you. In Python there are a lot of things that we can do, and I'm really amazed by all
the flexibility of Python and the access to the internals, and when I started discovering Python I was 16 years old, and I discovered you could play with exec and socket and combine them and make trojan horse, and I found that really cool. Many years later I discovered people were basically taking the same idea to make something
called Jupyter notebook, you may have heard about, something where you receive Python code over the internet and then you just execute it somewhere. And I really like tinkering with Python, I also created my own web framework before
they were Django and the most popular ones that are more mature now, and one thing I discovered is you could use exec file to execute code that's in a Python file in a specific environment. So you have these environment variables in your Python code, and you can override them, so when you execute code from a Python file in a specific environment, you pass the environment
in the dictionary above here, it can only access these variables, so you could restrict the access to imports, the access to a lot of things, but Python is Python and there are always ways to escape this by accessing the properties of objects, so there is no safe
way to run Python codes contained, like you could use Lua or something, when you have Python code it always has access to everything that the C Python interpreter has access to.
And so I will be talking about code reloading, which is the process of replacing part of a program with a new version. It focuses on changes in the source code, a term mostly used for interpreted languages, and you have two ways to do code reloading, you have cold code reloading, which means that you are stopping the process and you are restarting it, and you have hot code reloading
where you keep the process running and you just patch it while it's running, keeping the state. A small analogy, cold code reloading, you really have to, you can really switch anything you want, but it takes a while, hot code reloading, you just do it while it's going
on. Cold code reloading is the most popular technique for code reloading in Python, so you stop and restart the entire process, and it's easy, reliable, but you lose state, and getting it back might take a while, it's very popular in web frameworks because all
of the state is stored in databases. And you also have the easiest way to do code reloading that you all used, control C, up enter, I still end up using it so often, and there is tooling to do that, if you
look at how Django and Flask do it, you have good examples, the Django source code especially is very easy to understand, very clear, and the main part of it ends up being this part here, so it has this object called a reloader, and will run this function, and
it will basically run your main app, your main process, in a thread, and when there is something that has changed, it looks at changes in the file system, when it has changed, it will restart your program in that thread, so it's basically running all of Django in a thread, and that allows it to stop it, reimport everything, and then
restart that part of the process, so you still have one process running, only one process, but it's running all of your application in a background thread that's managed by this reloader object, that's what happens when you do a Python manage.py run server using Django, Flask uses something very similar, the code is a bit easier to read when
you look at Django, so really have a look at this autoreload from Django, it's a really good example on how to do things, and under the hood, there are different ways of detecting that a file has changed, of course you could just manually, every second or every
few second looks if there are changes on the file system from a thread again, but there are ways on different operating systems to have direct notifications, direct signals when something is happening. On Linux you have the inotify API provided by the kernel, that basically gives events so any process can look for changes on files, and the very
simple way to use it in a shell is just to use this inotify to wait, so you can just say I'm waiting for anything to happen in this directory, and then when something happens you just do something. In Python you have the py inotify binding to this inotify
library, and you have watchdog, and watchdog is a multiplatform library in Python to do exactly this, so it works on both Linux, MacOS, Windows, BSDs, not micropython, and the interface is quite simple, a bit of a classy overhead, but you can basically schedule
an observer and start it and it will use callbacks when something is modified, so you can do whatever you want to do at that moment. Hot code reloading, that's something that's less common, especially in Python, but in general it's less common, it has its drawbacks
because of the states, it can lead to inconsistent states, but it also has advantages because sometimes this state that you're interested in, you're making a video game and you have an NPC, and to test it you have to go back to that same space, recreate the whole
behavior, it's hard to tweak the behavior live when you have sometimes a web socket or socket connections that you have to keep open, when starting your program takes a while because you're initializing a bunch of things, then it can be quite useful to do hot code reloading. And there are two challenges if you want to do this, one of
the challenge is to find and load the new code and then you need to replace the references where that code is used in the code, in memory. And it's not a new thing, other languages do it, Java especially, they have a way to use the debugger and you can replace
a version of a class file which is the PYC of Java, the compiled file of Java, you can replace it from your IDE directly while it's running using the debugger, the only constraint is that the interface of the class, the methods, the properties of that class
may not change. In C++ you can do hot code reloading by using DLLs, the dynamic link libraries or shared libraries and that allows you to also do hot code reloading, so it's possible to replace DLLs and do hot code reloading in C++ as well.
If you want to do it in pure Python there are three main ways to execute code from Python, you have the import module, if you just import something it will basically execute everything in that file, if you were at the micro Python talk, sometimes you just import blink and it just runs it, that's a weird feature of Python but it basically
just executes everything there. You can use exec, this function that takes a string and then it will just parse the Python code from that string, execute it, potentially in a specific environment you can add specific variables to the environment of this execution
and you have eval, that's basically evaluating a Python expression and getting the result out of it. So if you want to make a small shell or ask for a user input and you use eval, the user can type any Python code he wants, which could lead to security issues if that's untrusted input, but it can be quite nice if you want the user to be
able to use powers of numbers with two stars and any Python supported expression. So if you want to use the import base, if you use import, something that you can
use is this imported reload and you can reload a Python module that you imported. So if you just have some code that's importing a module, you can reload that module, but all the references you have will still be to, if you have a reference or a function inside that module, it will still be a reference to the old function, to the bytecode
of the old function, not the updated version. However, if your module is named module and you have module.my function and you always call it module.my function, then when you reload it, if it can access the new reference to the new loaded module, then this will work. So it's quite an easy way to do reloading of codes if all your references
are not inside the module but reference always through the whole module.my function. One issue you have if you use this import base reloading is sometimes people write
code in the middle of the Python file. You just have a small script that's containing a while through or you have a connection to a database that takes 10 seconds and that's blocking and every time you're reloading, this will be in your way every time and will
slow you down. Another way you can do Python code reloading is to just take control yourself of the execution of the refreshing. So if you have some Python code here, you can execute the code above within the environment of this module, module.py.
So if I have a function my sum in module.py, by running this I can basically refresh it and say okay, now I want to replace module.my sum with the new function, the new code here. And so if you want to wrap everything together, what can be quite interesting
is to have a proxy. So instead of manipulating directly the function you're interested in, when you want to be able to reload something, you don't pass around a reference to that function but to a proxy that can change its own reference. That way you can hot reload
something and then everything that's using it is not using my function directly, it's using a proxy to my function. And you can override the call attribute. So here you have this proxy here and it's a proxy to work in progress. It keeps this reference to
the function and then every time you call the proxy, so the instance of this proxy, it will basically just forward every argument to the function and return the result. So you can just proxy a call to a function this way with a mutable reference to the function here. This mutable reference you can change later. You can do something similar with classes.
So if you look at a Python class, an instance of a class in Python, it basically has two very important attributes, class and dict. Class is a reference to the class that it
inherits from. So if you instantiate a class, every instance has a reference to the class it instantiates and that's referenced because Python is mutable so you can change the underscore underscore class of your object and forward it to another class and bam, your object is now from another class. I see some people don't like this idea. It's Python with great
powers and great responsibility. You can do very nice things in Python. Please do not unless it really makes sense. And you have this underscore underscore dict object which contains all the properties that are part of your instance. So if you do my instance
dot a, it will look, okay, is a defined inside underscore underscore dict? Yes, it will just return that. No, then it will look on the class and on the parents of that class for this attribute a, attribute or method because all the methods and the attributes
are in this underscore underscore dict part. So in practice if you have this, an object, an instance and you replace the underscore underscore class and you keep the underscore underscore dict, then you are switching, reloading what class that instance is part of and then
if that new version, new class is the new version of your class, you just reloaded live the instance and the code of your class. So this allows you to basically do hot code reloading of the class of all the instances if you patch this. And then
the last thing that's useful in Python is the weak references. So weak references allow you to keep a reference to an object but still allow it to be garbage collected. So when you have a reference, the garbage collector uses a reference counting and also
a regular collection for circular import, circular references but when you keep a weak reference to something, then it will not increase that garbage counting counter so it will allow the object that's targeted to be garbage collected at some point. And so you have
this weak ref library in Python to have a weak reference to something. You can get the source codes of the new source code using the inspect module in Python.
You have inspect.get source of my function and it will just return you directly the source code as a string of your function and inspect.get source of a class does exactly the same thing so it's very easy to get access to the source code of any Python function or class.
And if you put it all together, you can say okay, I have my source code, I just get the new source code of my targets which uses inspect.get source. Then I have some environment, the environment of the module and some new environment with the variables created by
the code and I just execute the source code within the environment of the module so you can access all the imports and all the variables defined in that module and I have this local so that every variable created during this execution is available within
this name space. And I just returned the field from this dictionary here that's named the same as my targets and now I just hot code reloaded it. Putting it together, I made a small module in Python where I added this autoreload feature
so you can decorate any function and it will reload it automatically while it's running. We can have a look at a small demo here. So I have some Python code with global state and two functions that are processing something and I will just, I can just run this.
But if I want to be able to live code, I need to, I can just reload those. I'll just mark these two methods as autoreload. So there's no magic. You have to mark them
as reloadable. Too much magic is bad. There is already enough. And so here on the right side, you see that it's executing this program. I don't really like the way it's printing the value so I want something more English like value is. And this complex processing thing, it's, yeah, maybe it was a 20 I wanted. So now
it's multiplying everything by 20 or it was a negative value so it's changing sign all the time. And this is just while running, I can just swap it directly and as you can see, the program is running. It's maintaining the state but you can still change the code.
If you do a typo, then it will just show you that there is an invalid syntax and wait for you to fix it to keep running again. And you can just pip install it. It's
on pipe UI so if you end up in the situation where your state is complex to get in Python and you want to work on something, you are not doing web with all your state in a database. Have a look at reloader. It's a simple way to just hot code reload and
trick your code while it's running. You can find the slides on this URL and thanks for watching. I believe we have five minutes for questions or remarks if you didn't like
it. So if you have any questions, don't hesitate. Yes?
So you can use AST. Yeah, so what you're saying is that the normal, let me just get a normal shell. So what you said is that the normal ways of executing Python code is not safe
but in the library AST, literal evil and this one should be safe to execute, right? This only allows to evaluate constant stuff. Okay, that's good to look at. Thank you.
So there is a library that allows you to do safe evaluation by filtering the opcodes that the code is allowed to run. That's what you're saying. Okay, that's quite interesting as well. Yeah. So how do you kill it? Phone calls?
Phone tools. Oh, okay. PWN tools. Okay. I'm not sure I want to pip install something with PWN inside. Yeah, any other question or remarks? Okay, thank you.