LSP[ruff_lsp] 버전 문제 해결하기

서제로·2024년 6월 29일
post-thumbnail

들어가며

나는 주 IDE나 에디터로 VSCode와 neovim을 번갈아 사용한다. 얼마 전부터 neovim을 사용하고 싶어서 python파일을 여는 순간, 이상한 팝업이 떠서 도저히 파일을 고칠 수가 없는 거다.

위와 같은 팝업이 한 글자를 칠 때마다 계속해서 떠서 온 화면을 가렸다. 이는 관련있다고 생각하는 `nvim`, `dotfiles` 등을 업데이트 해도, 가능한 모든 가상환경에서 파일을 열어도 똑같았다.

일은 해야 하니까, 문제를 해결해 보자.

문제 상황

오류 코드

LSP[ruff_lsp] Ruff <0.2.0,>=0.0.189 required, but found 0.2.2 at /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ruff

배경지식

What is ruff_lsp?

LSP

우선 LSP부터 알아보자. LSP는 Language Server Protocol의 약자로, 다양한 코드 편집기에서 언어 서비스 기능을 제공하는 데 사용되는 일반적인 프로토콜이다. 코드 편집기는 이 프로토콜을 API로 받아와 사용자로 하여금 자동 완성 기능, 단어 검색 기능 등을 제공한다.

ruff

Ruff는 rust로 작성된 python linter이자 code formatter이다. Python linter 따로, code formatter 따로 사용해야 했던 상황에서 2022년에 나온 ruff는 단독으로 사용해도 두 가지 기능 모두를 충족하기 때문에 현재는 Hugging Face, Pandas, Scipy와 같은 주요 오픈소스 프로젝트에서 사용되고 있다고 한다.

  • Linter : 소스 코드를 분석해서 형식적/논리적 오류가 있는지 검사하고 이를 발견하고 표시하는 도구로, 대표적인 린터로는 flake8, pylint, bandit 등이 있다. 이들은 검사 도구이기 때문에 여러 가지를 사용하면 오류를 더 엄격히 잡아낼 수 있다.
  • code formatter : 코딩 컨벤션(e.g. PEP-8 by Python)에 맞추어서 코드를 수정 및 정렬해주는 도구로, 대표적인 코드 포맷터로는 Black, YAPF 등이 있다. 줄, 띄어쓰기 간격이나 함수 선언 스타일과 같은 형식적인 구조를 수정해 줄 수 있다.

그러니까 ruff_lsp는 ruff를 도입하기 위한 언어 서버 프로토콜이라는 말이 된다.

상황 파악

ruff-lsp의 버전 문제

해결 구조

간단하지만 어려운 문제인, 버전 문제를 해결하면 끝난다.

  • 현재 vim을 실행시켰을 때 사용되는 ruff_lsp 버전이 0.2.2로 설정되어 있을 것이다.
  • 따라서 제일 이상적인 해결 방식부터 나열해 보자면 다음과 같다.

    1) VIM에서 설정되어 있는 버전을 변경
    2) 특정 버전만을 삭제
    3) ruff_lsp를 삭제하고 조건에 맞는 버전으로 다시 설치

이제 문제를 타고 가면서 1), 2), 3) 중 하나를 선택해보자.


문제 해결

1. 현재 ruff 버전 확인

>>> ruff --version
ruff 0.2.0

오잉? 0.2.2가 발견됐다면서 이렇게 뜬다.

2. directory 확인

>>> cd /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ && ls
...
ruff	ruff-lsp

찾았다 요놈!
그런데 이 파일의 확장자를 도무지 알 수 없어, 이 파일이 무엇인지 확인해준다. 그래야 삭제를 하던지 수정을 하던지 조치를 취할 수 있기 때문이다.

>>> file *
ruff:          ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=***, for GNU/Linux *.*.*, stripped
ruff-lsp:      Python script, ASCII text executable

여기서 Python script 형태로, 수정 가능한 ruff-lsp를 실행시켜 보자.

>>> vi ruff-lsp
#!/USER/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/python
import re
import sys
from ruff_lsp.__main__ import main     ■ Import "ruff_lsp.__main__" could not be resolved
if __name__ == '__main__':
	sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
	sys.exit(main())

오호..여기에서 문제가 발생했음을 알 수 있다.
1번 라인에서 이야기하는 경로 주변에서 ruff_lsp.__main__.py가 있는지 확인해 보자.

>>> cd /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/lib/python3.11/site-packages/ && ls
...
ruff
ruff_lsp
ruff_lsp_0.0.49.dist-info
ruff_lsp_0.2.2.dist-info

>>> cd ruff_lsp/ && ls
__pycache__  __main__.py  server.py    utils.py
__init__.py  py.typed     settings.py

우선 ruff_lsp.__main__.py가 있는 경로를 확인했고, 그 과정에서 메타데이터가 저장되어있는 ruff_lsp_0.2.2.dist-info 파일을 확인했다. 모종의 이유로 ruff_lsp가 설치되는 과정에서 0.0.49, 0.2.2 두 버전이 깔려있음을 추측할 수 있다. 파일을 삭제해보자.

>>> cd ruff_lsp_0.2.2.dist-info/ && ls
licenses   METADATA  REQUESTED  entry_points.txt
INSTALLER  RECORD    WHEEL

>>> file *
INSTALLER:        ASCII text
METADATA:         ASCII text
RECORD:           CSV text
REQUESTED:        empty
WHEEL:            ASCII text
entry_points.txt: ASCII text
licenses:         directory

>>> rm -r -rf ruff_lsp_0.2.2.dist-info/

하지만 이 파일을 삭제했다고 해서 문제가 해결되지는 않았다. 다시 경로를 타고 원래의 목표였던 파일을 살펴보기로 한다. 여기서 문제 해결의 새로운 단서 발견.

>>> cd /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/lib/python3.11/site-packages/ruff_lsp/
>>> vi server.py
...
def _find_ruff_binary(
	settings: WorkspaceSettings, version_requirement: SpecifierSet | None
) -> Executable:
   """Returns the executable along with its version.
   If the executable doesn't meet the version requirement, raises a RuntimeError and
   displays an error message.
   """
   path = _find_ruff_binary_path(settings)
   
   version = _executable_version(path)
   if version_requirement and not version_requirement.contains(
      version, prereleases=True
   ):
      message = f"Ruff {version_requirement} required, but found {version} at {path}"
      show_error(message)
      raise RuntimeError(message)
   log_to_output(f"Found ruff {version} at {path}")
   
   return Executable(path, version)
   ...

다음과 같이 나의 문제점은 저 함수에 따라 에러로 출력된 것이었음을 확인하였다. 그럼 이 상황에서 할 수 있는 것은 다음과 같다.

  • _find_ruff_binary_path 함수 확인
  • _executable_version 함수 확인
  • 터미널에서 /path/to/ruff --version 실행

특히 세번째 방법을 확인했다면 문제 상황을 더욱 더 빨리 인식하고 경로를 타고 타고 가서 삭제해줄 수는 있었지만, 나는 조금 더 빠른 방법이 필요했다. (물론 결과적으로는 나의 방식이 문제를 해결하는 데에는 도움이 되었을지는 모르지만 다른 문제를 야기할 줄은 몰랐다..)

3. configuration file 확인

경로를 타고 타고 가다가 깨달은 사실인데, 사실 이 문제는 VSCode로 파일을 실행했을 때에는 일어나지 않았다. 에러가 말하고 있는 경로가 설명하듯, 근본적으로는 nvim의 버전 적용 문제이므로 이와 관련한 configuration file들을 확인해 본다.

>>> nvim ~/.config/nvim/
...
>>> vi ~/.vimrc
...
" A handy way to set (global) makeprg.
" See ~/.dotfiles/nvim/lua/config/commands/Makeprg.lua for nvim+lua version
command! -nargs=1 Makeprg       let &makeprg="<args>" | echo "&makeprg = <args>"
command! -nargs=1 MakeprgGlobal let &g:makeprg="<args>" | echo "&g:makeprg = <args>"
command! -nargs=1 MakeprgLocal  let &l:makeprg="<args>" | echo "&l:makeprg = <args>"
...
>>> cd ~/.dotfiles/nvim/lua/config/commands/ && ls
AutoBuild.lua  Config.lua  Defer.lua  Makeprg.lua  init.lua
>>> vi Makeprg.lua
...

하지만 별다른 소득은 없었다.
그래서 해결책 3)을 채택하기로 했다. 지금 당장의 나에게는 빨리 해결하는 것이 중요했기 때문에.

4. 관련 파일 삭제

이 이후에 생길 다른 문제들을 감수하고 관련 파일들을 삭제해보기로 한다.

>>> cd /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/bin/ && ls
...
ruff	ruff-lsp
>>> rm -rf ruff ruff-lsp
>>> pip3 install ruff-lsp

이렇게 한 결과, 문제는 해결되었다. 하지만 또다른 문제를 야기하게 되는데..이것은 다음 포스팅으로 정리해 보겠다.


문제 회고

궁금증 정리

neovim vs nvim ?

  • nvim : neovim의 커맨드 네임.

vim vs neovim ?

결론부터 말하자면, vimneovim은 다르다!

  • vim : 대부분의 Linux 계열 운영체제에 기본적으로 설치되어 있는 텍스트 에디터
  • neovim : vim의 단점을 보완하고, 추가적인 특징들을 추가한 텍스트 에디터. 특히 vim에는 호환되지 않는 다양한 플러그인들을 지원하는데, 이 중 하나가 LSP이다.

내가 생각한 문제가 맞았나?

  • 이 문제는 ruff_lsp 문제가 맞기도 하지만, 근본적으로는 설정한 neovim 사용 환경 문제.

왜 문제가 생겼을까?

사실, 정확한 이유는 알 수 없었다. 다만 평소에는 일어나지 않았던 문제인데 갑자기 문제가 발생했다는 점에서 다음과 같은 추측들을 해볼 수 있겠다.

  • neovim을 여러 번 설치했을 경우. 확인해보니 neovim을 설치하기 이전, 오래 전에 설치했던 dotfiles를 통해 이미 neovim이 깔려 있었다. 가상환경 불일치 등의 모종의 이유로 몇 번 더 neovim이 설치되었다면 이런 문제가 발생할 수 있을 것이다.
  • ruff_lsp를 여러 번 설치했을 경우. 사실 이 가능성이 제일 높은데, 설치를 어떤 환경에서 할지 고려하지 않고 중복해서 설치했다면, 버전의 디폴트 값이 바뀌는 상황을 포착하지 못할 수 있다. 이 가능성은 다음과 같은 결과를 보고, 본 문제와 연관이 있을 것이라는 추측을 통해 선정되었다.
>>> cd /USER/.local/share/nvim/mason/packages/ruff-lsp/venv/ && ls
bin  include  lib  lib64  pyvenv.cfg
>>> vi pyvenv.cfg
home = /USER/.miniconda3/envs/VENV/bin
include-system-site-packages = false
version = 3.11.7
executable = /USER/.miniconda3/envs/VENV/bin/python3.11
command = /USER/.miniconda3/envs/VENV/bin/python3 -m venv /USER/.local/share/nvim/mason/packages/ruff-lsp/venv

우선 VENV라는 해당 가상환경은 자주 사용하지 않는 가상환경일뿐더러, pyvenv.cfg에 이 가상환경의 정보만 있다는 것이 이상하다. 또한 VENV 가상환경을 활성화 시킨 상태에서도 이 문제는 유효했기 때문에, 관련 툴들의 설치에 이 가상환경의 활성화 여부가 관련되었을 가능성이 있을 것이다.

관련 툴 설치 방법

번외로, 본 문제 해결 과정에서 사용되었던 툴들은 다음과 같이 설치할 수 있었다.

  • ruff installation
pip install ruff
  • ruff-lsp installation
pip install ruff-lsp
  • neovim 설치 방법 in Ubuntu (번외)
sudo apt install neovim

결론

물론 상황을 완벽히 고쳤다고 볼 수 없다. 파일을 삭제하는 것이 문제 해결의 최종 목표였다면, 이렇게 공부해 가면서 하지 않아도 되었을 것이다. 보다 문제의 본질을 파악하고, 해당 문제에 대한 해결책을 찾는 것이 목적이었다. 결국 시간적인 한계로 삭제를 감행했지만. 또한 부수적인 경고 팝업이 뜨는 문제가 발생했기도 하고.
이 문제를 해결하면서 다음을 깨달았다.

  • pip, apt-get, conda activate 상태에서의 pip, apt-get등 각종 툴들이나 라이브러리, 모듈들을 설치하는 방법은 다양하다. 따라서 가능한 한 이를 구분짓는 자체 시스템을 구축해야겠다.

그러지 않으면 저장되어있는 공간이 달라서 이렇게 문제가 생겼을 때 여기저기 경로들에 들어가서 파일들을 확인해 봐야 하는 문제가 발생할 것이기 때문이다.

문제의 원인을 온전히 파악해서 완벽히 해결하는 날까지 해결의 과정을 회고하겠다는 다짐을 하며, 다음 포스팅에서는 아직 남아있는 문제를 어떻게 해결하였는지 살펴볼 것이다.

References

profile
고군분투의 흔적들. 매번 똑같은 문제를 똑같이 검색하기 귀찮은 것들과 함께 보면 좋은 것들을 함께 작성합니다.

0개의 댓글