Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tramp support? #30

Open
mtekman opened this issue May 20, 2019 · 19 comments
Open

Tramp support? #30

mtekman opened this issue May 20, 2019 · 19 comments

Comments

@mtekman
Copy link

mtekman commented May 20, 2019

Is there any interest in getting this to work on remote machines via TRAMP?

I find that "conda-env-list" does not produce any output

@necaris
Copy link
Owner

necaris commented May 25, 2019

@mtekman that's certainly an interesting idea, although I'm personally not sure what you would want to use it for remotely. When you activate a conda environment remotely, all of the functionality you might get from it (e.g. interactive Python REPL, documentation & IDE support) would have to be sent to / from the server, which doesn't sound very appealing to me.

Can you share some more about your use case, so I can understand better what functionality you'd like to have over the remote connection?

@mtekman
Copy link
Author

mtekman commented May 28, 2019

@necaris When debugging an R script via ESS-mode (which works in TRAMP) the mode defaults to using the default path, which does not contain the conda packages I need to properly debug the script.

@necaris
Copy link
Owner

necaris commented May 28, 2019

@mtekman thank you for the clarification! I don't know ESS personally, but from a quick search it seems like when running ESS over Tramp, Emacs is connecting to a remote R process via SSH, rather than running shell commands on the remote server?

@mtekman
Copy link
Author

mtekman commented May 28, 2019

@necaris By my understanding, it spawns an R process over SSH on the remote server, and all local instructions are transferred to and then executed on the remote server, and then the results are transported back to the local machine. Controlling what conda environment the local remote machine uses is the problem for me.

Edit: Corrected machine

@necaris
Copy link
Owner

necaris commented Jun 7, 2019

@mtekman now I'm confused. Do you need to have the conda environments on the remote machine, where the script is running, or the local machine where you might want to debug the results?

The way it works right now, using the appropriate conda environment locally should be as easy as calling conda-env-activate. j

However, I'm not sure how to start telling Emacs / ESS-mode how to activate the conda environment on the remote machine...

@mtekman
Copy link
Author

mtekman commented Jun 19, 2019

Apologies, on the remote machine.

@Qwarctick
Copy link
Contributor

Interestered for this feature too.
Imo, the conda env must be on the remote machine. You connect to it with Tramp and you should be able to conda-env-list all envs on the machine.

It could be a great improvment :)

@jsigman
Copy link
Contributor

jsigman commented May 29, 2020

Also interested in this. I'm envisioning activating a conda env on a remote machine so that I can use a remote python language server with lsp-mode, and that path variables would be correct.

@mtekman
Copy link
Author

mtekman commented May 29, 2020

I was poking through the source a few days ago, and one issue is that the current package looks for a specific conda installation directory:

(defcustom conda-env-home-directory "~/anaconda"
  "Location of the directory containing the environments directory."
  :type 'directory
  :group 'conda)

Given that different remote machines are configured differently, there is no catch-all solution to setting this for hosts, unless the user sets this for all their hosts:

(defcustom conda-env-home-directory 
  '((home . "~/anaconda") (remote1 . "~/miniconda") (remote2 . "~/miniconda3"))
  "Alist of locations containing the environments directory."
  :type 'alist
  :group 'conda)

Either the package looks in all these locations for an envs folder (when TRAMP is active), or it maps very specifically to the hostname or ip which should appear in the above list.

@necaris
Copy link
Owner

necaris commented May 31, 2020

@jsigman just for clarification, you're saying you'd like to activate the conda environment and run the language server all on the remote machine, and communicate with it from your local Emacs? This seems similar to @mtekman's use case of wanting to run exclusively remote commands -- so we're basically saying that all of the "conda activate" (etc) commands we need to run have to run on the remote end of a specific TRAMP connection?

I'm wondering if we can use something like the solution described here to activate an environment when the TRAMP connection is established, and otherwise use builtin Emacs methods for running commands within the (conda-activated) TRAMP session.

@f-kretschmer
Copy link

lsp and the python-shell-interpreter already work with tramp when conda-env-current-dir is set manually, with

;; remove "/ssh:host:" from conda dir
(setq conda-tramp-path (replace-regexp-in-string ".*:" ""
                                           (format "%s/bin" conda-env-current-dir)))
(add-to-list 'tramp-remote-path conda-tramp-path)
(add-to-list 'tramp-remote-path 'tramp-own-remote-path) ;; for some strange emacs reason this has to be added, otherwise tramp-remote-path is ignored

(tramp-cleanup-current-connection probably has to be called for the tramp path to be applied)

So the functionality, that would have to be added, is:

  • change conda-env-list to work differently when on remote host: (conda-env-candidates-from-dir "/ssh:host:~/miniconda3/envs") already works, conda-env-default-location would have to be changed
  • change conda-env-activate to be able to select a remote environment (changing the conda-env-default-location function should be enough) and execute the code at the top to adapt the tramp path
  • undo the changes to tramp-remote-path when conda-env-deactivate is called

I don't know about all the other functionality though, e.g., eshell ...

@jsigman
Copy link
Contributor

jsigman commented Jun 1, 2020

@necaris You are understanding correctly, and I would love to try to help with this. For me, it's about getting lsp-mode to work on a restrictive server. I might try to catch up on this package some more this weekend.

@necaris
Copy link
Owner

necaris commented Jun 1, 2020

@fcupo thank you for doing all that research! So it looks like we need to:

  • find a nice user experience to set the conda-env-default-location, e.g. perhaps setting an alist of known remote hosts as @mtekman suggested
    • (please bear in mind my concern here is making this convenient while not making the common case of local environments any less convenient)
  • update the conda-env-default-location to return the tramp-enabled path if that's appropriate
  • update the activation and deactivation functions to know about tramp paths and execute the appropriate setup / teardown code when they encounter one

@necaris
Copy link
Owner

necaris commented Jun 28, 2022

Anyone still interested in helping with this?

@renzmann
Copy link

renzmann commented Sep 9, 2022

@necaris Looks like it's been a minute since there was some discussion on this, but I'm chiming in to say I have the exact same use case as @jsigman, and came across this thread looking for solutions. No promises at the moment, but assuming I find some time and have strong enough emacs-fu I'd love to help out

@necaris
Copy link
Owner

necaris commented Sep 11, 2022

@renzmann let me know if you need any help!

@renzmann
Copy link

renzmann commented Sep 16, 2022

Hey @necaris! Before I tried to change up too many things I thought I'd take down some notes for potential discussion.

I think the main barrier to TRAMP right now would be the caching mechanism, like when we poll for conda--executable-path

conda.el/conda.el

Lines 116 to 122 in cb9544e

(defvar conda--executable-path nil
"Cached copy of full path to Conda binary. Set for the lifetime of the process.")
(defun conda--get-executable-path ()
"Return full path to Conda binary, or throw an error if it can't be found. Cached for the lifetime of the process."
(if (not (eq conda--executable-path nil))
conda--executable-path

This setup assumes a pretty different working model than how I and some of the users above would be using conda, which would have a different executable path to conda for each connection. So I might have a dozen or so different conda executables I'm working with, depending on which machine the current buffer is located on. I think a fair assumption here, though, would be that an executable path is unique to each connection, so we could maybe take an alist of TRAMP prefixes and cache those:

;; Made up executable paths
(setq conda--executable-path-alist
      '(("/ssh:my-host:" . "/usr/local/bin/conda")
        ("/ssh:another-host:" . "~/.conda/bin/conda")
        (nil . "/opt/conda/bin/conda"))

I think we can reliably get the different hostnames via (file-remote-p default-directory), but I'm not sure if there are any footguns hidden in there about the way file-remote-p or default-directory work. We also just need to add a 'remote to how we find the conda/mamba executable:

(defun conda--get-executable-path ()
  ;; caching steps omitted for brevity
  (cond
   ((executable-find "conda" 'remote))
   ((executable-find "mamba" 'remote))))

With that step out of the way, I think it's then a matter of patching variables with the correct connection/host component to get the downstream components working, as mentioned above.

@necaris
Copy link
Owner

necaris commented Sep 18, 2022

@renzmann that all seems sensible to me! The main concern I have is that it shouldn't require any extra work for the common case of a local environment 😄

@claytharrison
Copy link

claytharrison commented Jan 16, 2024

Hi, I'm an elisp beginner so unfortunately this isn't much of a contribution towards a robust solution of this issue in the package, but I wanted to put my workaround here for anyone else who happens to come here needing one. (with thanks to @f-kretschmer for their comment here a few years back)

I haven't tested this extensively with eglot, etc, my only use-case is to be able to execute org-mode src blocks in the correct remote environment, and it works for that. I think it's using the remote language server for eglot too but I'm not entirely sure to easily tell if that's the case. Maybe it will work for the other stuff too.

(setq remote-conda-anaconda-home-list
      '(("remote01" "/ssh:me@remote01:/home/me/conda/special_conda")
        ("remote02" "/ssh:me@remote02:/home/me/miniforge3")))

(setq remote-conda-env-home-directory-list
      '(("remote01" "/ssh:me@remote01:/home/me/conda/special_conda")
        ("remote02" "/ssh:me@remote02:/home/me/miniforge3")))

(setq remote-conda-env-subdirectory-list
      '(("remote01" "envs")
        ("remote02" "envs")))

(defun conda-tramp-activate (conda-env)
        "Activate a conda environment on a remote host."
        (interactive "sConda environment: ")
        (let ((hostname (file-remote-p default-directory 'host)))
                (setq conda-anaconda-home (cadr (assoc hostname remote-conda-anaconda-home-list)))
                (setq conda-env-home-directory (cadr (assoc hostname remote-conda-env-home-directory-list)))
                (setq conda-env-subdirectory (cadr (assoc hostname remote-conda-env-subdirectory-list)))

                ;; check if dir conda-env exists in conda-env-home-directory/conda-env-subdirectory
                (if (not (file-exists-p (f-join conda-env-home-directory conda-env-subdirectory conda-env)))
                        (error "Conda environment %s does not exist on remote host %s" conda-env hostname))

                (setq conda-env-current-path (f-join conda-anaconda-home conda-env-subdirectory conda-env))
                (setq conda-tramp-path (replace-regexp-in-string ".*:" ""
                                                        (format "%s/bin" conda-env-current-path)))
                (add-to-list 'tramp-remote-path 'tramp-own-remote-path) ;; need to do this BEFORE the next line or else python won't find the right remote env and will just use the base env
                (add-to-list 'tramp-remote-path conda-tramp-path)
                (tramp-cleanup-this-connection))
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants