GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Implement remote evaluation and distributed garbage collection by jonatanklosko · Pull Request #46 · livebook-dev/pythonx

Conversation

@jonatanklosko

@jonatanklosko jonatanklosko commented Mar 2, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This adds Pythonx.remote_eval/4 to run Python code on another node. Remote eval returns "remote" %Pythonx.Object{} structs and we ensure the corresponding Python objects are kept alive on the owner node as long as necessary. There is Pythonx.copy_remote_object/1 that serializes the remote object and then deserializes it locally. Pythonx.Object also implements the FLAME.Trackable protocol, which sets up proper lifetime tracking for objects returned from FLAME.call/3 (similarly to Pythonx.remote_eval/4.

More details on the API in Pythonx docs, and implementation details in Pythonx.ObjectTracker.

We could have a separate Pythonx.Remote module, but the API surface is small, so I am not sure if we necessarily need that.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@jonatanklosko
Implement remote evaluation and distributed garbage collection
914cdc2
@jonatanklosko

jonatanklosko commented Mar 2, 2026

Copy link
Copy Markdown
Member Author

Currently Pythonx.remote_eval/4 returns the globals map. The user may not necessarily care about all of those, so tracking those just to be GCed immediately is unnecessary work. That said, it's likely fine for the use cases where Pythonx.remote_eval/4 would be used, such as notebooks. When using Pythonx in an actual app, the evaluation should rather be behind a GenServer API, and remote objects should not be at play. If we ever discover it's a problem, we can also add an option to prune/pick the returned globals.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jonatanklosko
jonatanklosko commented Mar 2, 2026
View reviewed changes
Comment thread lib/pythonx/error.ex
"""

defexception [:type, :value, :traceback]
defexception [:lines]

@jonatanklosko jonatanklosko Mar 2, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it's a breaking change, though ideally I would consider the exception fields to be opaque in this case.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@jonatanklosko jonatanklosko Mar 2, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR. I changed it so that we can easily pass Pythonx.Error across nodes without worrying about tracking and going back to the owner for formatting. I don't think storing the separate type, value, traceback objects was a good idea in the first place - if someone is interested in those, they should just have the try-catch in the Python code.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@josevalim

josevalim commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Currently Pythonx.remote_eval/4 returns the globals map

Perhaps we should make it so it will only return the last line? And if they want everything, they can call globals()?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

josevalim
josevalim reviewed Mar 2, 2026
View reviewed changes
Comment thread c_src/pythonx.cpp Resolved

Uh oh!

There was an error while loading. Please reload this page.

josevalim
josevalim reviewed Mar 2, 2026
View reviewed changes
Comment thread c_src/pythonx.cpp
if (enif_whereis_pid(caller_env, janitor_name, &janitor_pid)) {
auto device = type == 0 ? eval_info.stdout_device : eval_info.stderr_device;
// Copy the device term is from a differnet env, so we copy it into
// the message env, otherwise we may run into unexpected behaviour.

@josevalim josevalim Mar 2, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which sort of unexpected behaviour?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@jonatanklosko jonatanklosko Mar 2, 2026

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far it has been working fine. Once I was testing remote eval, where stdout_device and stderr_device are remote pid terms, I run into a weird issue where the message we send below would include a random term (e.g. :infinity, {}) instead of the actual pid. I then revisited the code and realised that those terms are from a different env and doing the copy first fixed the issue. It's interesting that it's only remote pid terms that revealed the issue.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@jonatanklosko

jonatanklosko commented Mar 2, 2026

Copy link
Copy Markdown
Member Author

Currently Pythonx.remote_eval/4 returns the globals map

Perhaps we should make it so it will only return the last line? And if they want everything, they can call globals()?

Similarly they could return a tuple with the specific values they need. However, the issue with those approach is that there is no way to return separate values to Elixir side, it would be a single %Pythonx.Object{}. And this is relevant, because in some cases they may want to do Pythonx.Decode, but only on one of those globals.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

josevalim
josevalim approved these changes Mar 2, 2026
View reviewed changes

@josevalim josevalim left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful! 😍

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@josevalim

josevalim commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Similarly they could return a tuple with the specific values they need. However, the issue with those approach is that there is no way to return separate values to Elixir side, it would be a single %Pythonx.Object{}.

Ok, so we are back to square one... with the difference we would be holding fewer things in memory?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jonatanklosko

jonatanklosko commented Mar 2, 2026

Copy link
Copy Markdown
Member Author

Similarly they could return a tuple with the specific values they need. However, the issue with those approach is that there is no way to return separate values to Elixir side, it would be a single %Pythonx.Object{}.

Ok, so we are back to square one... with the difference we would be holding fewer things in memory?

I'm not following. My point is that if we don't return globals, it becomes an actual limitation.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jonatanklosko and others added 2 commits March 2, 2026 22:39
@jonatanklosko @josevalim
Update c_src/pythonx.cpp
e0baf81
Co-authored-by: José Valim <[email protected]>
@jonatanklosko
gc self
51b1de0
@jonatanklosko
jonatanklosko merged commit 2f725ff into main Mar 2, 2026
9 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@jonatanklosko
jonatanklosko deleted the jk-remote branch March 2, 2026 21:53
This was referenced Mar 6, 2026
Add type, value, traceback objects back to Pythonx.Error #47
Merged
Future work on the Python integration livebook-dev/livebook#2937
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@josevalim josevalim josevalim approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@jonatanklosko @josevalim
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.