I also use it in a multiple dispatch library (https://github.com/breuleux/ovld) to replace the entry point by specialized dispatch code in order to cut some overhead.
It's fun.
Also, why is every damn post these days somehow framed in an AI context? It's exhausting.
Every 5/10-year segment of my life has somehow had one or two "This is the future"-hypes running concurrently with my life. Previously it was X, now it's Y. And most of the times, everything else is somehow connected to this currently hyped subject, no matter if it's related or not.
The only thing I've found to be helpful is thinking about and changing my perspective and framing about it. I read some article like this which is just tangentially related to AI, but the meat is about something else. So mentally I just ignore the other parts, and frame it in some other way in my head.
Suddenly people can write their articles with attachments to the hyped subject but I don't mind, I'm reading it for other purposes and get other takeaways that are still helpful. A tiny Jedi mind-trick for avoiding that exhaustion :)
(idk if this author is “junior” per se, mostly just agreeing the shift in perspective is helpful to not get burnt out by things like this)
Some of it gets really annoying on the business side, because companies like Gartner jump on the trends, and they have enough influence that businesses have to pay attention. When serverless was a thing, every cloud provider effectively had to add serverless things even if it made zero sense and no customers were asking for it, simply to satisfy Gartner (and their ilk) and be seen as innovating and ahead of the curve. Same thing happened with block chain, and is currently happening with AI.
Some minor details. You currently aren't updating functions if their freevars have changed, you can actually do that by using c-api to update __closure__ which is a readonly attribute from python:
ctypes.pythonapi.PyFunction_SetClosure.argtypes = [ctypes.py_object, ctypes.py_object]
ctypes.pythonapi.PyFunction_SetClosure(old, new.__closure__)
Also I think you should update __annotations__, __type_params__, __doc__, and __dict__ attributes for the function.Rather than using gc.get_referrers I just maintain a set for each function containing all the old versions (using weakref so they go away if that old version isn't still referenced by anything). Then when a function updates I don't need to find all references, all references will be to some old version of the function so I just update that set of old functions, and all references will be using the new code. I took this from IPython autoreload. I think it is both more efficient than gc.get_referrers, and more complete as it solves the issue of references "decorated or stashed in some data structure that Jurigged does not understand". The code for that is here: https://codeberg.org/sczi/swanky-python/src/commit/365702a6c...
hot reload for python is quite tricky to fully get right, I'm still missing plenty parts that I know about and plan on implementing, and surely plenty more that I don't even know. If you or anyone else that's worked on hot reload in python wants to talk about it, I'm happy to, just reach out, my email is visible on codeberg if you're signed in.
I'm not sure what you mean by "maintaining a set of old versions". It's possible I missed something obvious, but the issue here is that I have the code objects (I can snag them from module evaluation using an import hook)... but I do not have the function objects. I never had them in the first place. Take this very silly and very horrible example:
def adder(x):
def inner(y):
return x / y
return inner
adders = {}
def add(x, y):
adders.setdefault(x, adder(x))
return adders[x](y)
The adders dictionary is dynamically updated with new closures. Each is a distinct function object with a __code__ field. When I update the inner function, I want all of these closures to be updated. Jurigged is able to do it -- it can root them out using get_referrers. I don't see how else to do it. I quickly tested in a Jupyter notebook, and it didn't work: new closures have the new code, but the old ones are not updated.yea i've been meaning to do this for a while as well...
I do wish there were callbacks I could subscribe to that would notify me whenever my file changed or whenever any code changed, so I could re-run some init.
My other feature request would be a way to replace function implementations even when they are currently on the stack, as some other hot reload implementations can. But I certainly understand why this would be difficult.
Thank you a bazillion for making it. It works quietly in the background without fuss, and I'm grateful for it every time I use it.
It’s even in the real world now - most of my conversations with people in tech end up at AI eventually.
It kind of reminds me of the 2010s when non-tech people would ask me about crypto at social events.
That's what Lisp is!
Once you see how cool that is, then you can begin to appreciate why Lisp was the defacto standard for AI programing all the way back in the 1960s!
So whenever you want, you can start using "normal code" for manipulating the "normal code" itself, and hopefully now we have yet another perspective on the same thing, for why Lisps are so awesome :)
Dynamic metaprogramming is flexible but dangerous. Python is also "dynamic", meaning that code can be changed at runtime instead of only being able to accidentally pass null function pointers.
Python's metaclasses function similarly to Lisp's macros but in a consistent way: most Python code uses the standard metaclasses so that the macros don't vary from codebase to codebase. The Django "magic removal" story was about eliminating surprise and non-normative metaclassery, for example.
Does this tool monkey patch all copies of a function or just the current reference? There are many existing monkey patching libraries with tests
Someone explain to me, an old, aging programmer old enough to know UML, why this isn't some we presume very young person who has no idea how to write OOP coming up with some horrible convoluted way to do something routine?
Oh don't worry... they still cram that down our throats in CS undergrad in one of the courses.... Forget which one. I did my UG from 2020-24
;)
Just cause you can do something doesn't mean you should. I send thoughts and prayers for the people debugging programs where this is in place.
Wasn't SMC one of the LISP-associated AI fields a few decades ago? iirc it's been mostly abandoned due to security issues, but some of it survives in dynamic compilation.
Next step, here's how to load modules, resolve a dependency. Handle capabilities and dynamically inject more functionality 'live'.
Patching running machine code in memory for compiled objects is the same but you just need to work around the abstraction that is introduced by languages trying to make the whole stack human parseable.
For example, there's java.lang.reflect.Proxy [1] and System.Reflection.Emit [2].
[1]: https://docs.oracle.com/javase/8/docs/technotes/guides/refle...
[2]: https://learn.microsoft.com/en-us/dotnet/api/system.reflecti...
setattr(mod, name, new_func)
I once inherited a nice library which made heavy use of such compile/exec pairs. The code looked very convoluted and was very bug prone. I replaced all such pairs with lambda wrappers. This was much more readable: my colleagues who were also working on this code were like "oh is that what that was meant to be doing" after this change.
Creating a debugger for an LLM (not a human) is something I haven't really seen, but seems super useful...?
https://github.com/eidorb/ubank/blob/master/soft_webauthn_pa...