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

Allow conda autoupdate #346

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ All the ``fortls`` settings with their default arguments can be found below
"incremental_sync": false,
"sort_keywords": false,
"disable_autoupdate": false,
"allow_conda_autoupdate": false,
"debug_log": false,

"source_dirs": ["./**"],
Expand Down
8 changes: 7 additions & 1 deletion fortls/fortls.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@
},
"disable_autoupdate": {
"title": "Disable Autoupdate",
"description": "fortls automatically checks PyPi for newer version and installs them.Use this option to disable the autoupdate feature.",
"description": "fortls automatically checks PyPi for newer version and installs them. Use this option to disable the autoupdate feature.",
"default": false,
"type": "boolean"
},
"allow_conda_autoupdate": {
"title": "Allow Conda Autoupdate",
"description": "By default, fortls does not permit autoupdating in conda environments. Use this option to enable fortls to automatically check conda-forge for newer version and install them.",
"default": false,
"type": "boolean"
},
Expand Down
11 changes: 10 additions & 1 deletion fortls/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,16 @@ def cli(name: str = "fortls") -> argparse.ArgumentParser:
action="store_true",
help=(
"fortls automatically checks PyPi for newer version and installs them."
"Use this option to disable the autoupdate feature."
" Use this option to disable the autoupdate feature."
),
)
parser.add_argument(
"--allow_conda_autoupdate",
action="store_true",
help=(
"By default, fortls does not permit autoupdating in conda environments."
" Use this option to enable fortls to automatically check conda-forge"
" for newer version and install them."
),
)
# XXX: Deprecated, argument not attached to anything. Remove
Expand Down
105 changes: 77 additions & 28 deletions fortls/langserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
self._config_logger(request)
self._load_intrinsics()
self._add_source_dirs()
if self._update_version_pypi():
if self._update_version():
self.post_message(
"Please restart the server for the new version to activate",
Severity.info,
Expand Down Expand Up @@ -1590,6 +1590,9 @@
self.disable_autoupdate = config_dict.get(
"disable_autoupdate", self.disable_autoupdate
)
self.allow_conda_autoupdate = config_dict.get(
"allow_conda_autoupdate", self.allow_conda_autoupdate
)

# Autocomplete options -------------------------------------------------
self.autocomplete_no_prefix = config_dict.get(
Expand Down Expand Up @@ -1760,8 +1763,8 @@
schar = echar = 0
return uri_json(path_to_uri(obj_file.path), sline, schar, sline, echar)

def _update_version_pypi(self, test: bool = False):
"""Fetch updates from PyPi for fortls
def _update_version(self, test: bool = False):
"""Fetch updates from PyPi or conda-forge for fortls

Parameters
----------
Expand All @@ -1778,7 +1781,8 @@
request = urllib.request.Request("https://pypi.org/pypi/fortls/json")
with urllib.request.urlopen(request) as resp:
info = json.loads(resp.read().decode("utf-8"))
remote_v = version.parse(info["info"]["version"])
remote_v_str = info["info"]["version"]
remote_v = version.parse(remote_v_str)
# Do not update from remote if it is a prerelease
if remote_v.is_prerelease:
return False
Expand All @@ -1788,36 +1792,81 @@
"A newer version of fortls is available for download",
Severity.info,
)
# Anaconda environments should handle their updates through conda
if os.path.exists(os.path.join(sys.prefix, "conda-meta")):
return False
self.post_message(
f"Downloading from PyPi fortls {info['info']['version']}",
Severity.info,
)
# Run pip
result = subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"fortls",
"--upgrade",
"--user",
],
capture_output=True,
)
if result.stdout:
log.info(result.stdout.decode("utf-8"))
if result.stderr:
log.error(result.stderr.decode("utf-8"))
return True
return self._update_version_conda(remote_v_str)

Check warning on line 1796 in fortls/langserver.py

View check run for this annotation

Codecov / codecov/patch

fortls/langserver.py#L1796

Added line #L1796 was not covered by tests
return self._update_version_pypi(remote_v_str)
# No internet connection exceptions
except (URLError, KeyError):
self.post_message("Failed to update the fortls", Severity.warn)
return False

def _update_version_pypi(self, version: str):
"""Fetch updates from PyPi for fortls

Parameters
----------
version : str
version to install, only used for update notification
"""
self.post_message(
f"Downloading from PyPi fortls {version}",
Severity.info,
)
# Run pip
result = subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"fortls",
"--upgrade",
"--user",
],
capture_output=True,
)
if result.stdout:
log.info(result.stdout.decode("utf-8"))
if result.stderr:
log.error(result.stderr.decode("utf-8"))
return result.returncode == 0

def _update_version_conda(self, version: str):
"""Fetch updates from conda-forge for fortls

Parameters
----------
version : str
version to install, only used for update notification
"""

if not self.allow_conda_autoupdate:
return False

Check warning on line 1844 in fortls/langserver.py

View check run for this annotation

Codecov / codecov/patch

fortls/langserver.py#L1843-L1844

Added lines #L1843 - L1844 were not covered by tests

self.post_message(

Check warning on line 1846 in fortls/langserver.py

View check run for this annotation

Codecov / codecov/patch

fortls/langserver.py#L1846

Added line #L1846 was not covered by tests
f"Downloading from conda-forge fortls {version}",
Severity.info,
)
# Run conda
result = subprocess.run(

Check warning on line 1851 in fortls/langserver.py

View check run for this annotation

Codecov / codecov/patch

fortls/langserver.py#L1851

Added line #L1851 was not covered by tests
[
"conda",
"update",
"-c",
"conda-forge",
"--yes",
"--quiet",
"fortls",
# "conda-forge::fortls",
],
capture_output=True,
)
if result.stdout:
log.info(result.stdout.decode("utf-8"))
if result.stderr:
log.error(result.stderr.decode("utf-8"))
return result.returncode == 0

Check warning on line 1868 in fortls/langserver.py

View check run for this annotation

Codecov / codecov/patch

fortls/langserver.py#L1864-L1868

Added lines #L1864 - L1868 were not covered by tests


class JSONRPC2Error(Exception):
def __init__(self, code, message, data=None):
Expand Down
27 changes: 12 additions & 15 deletions test/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
def test_command_line_general_options():
args = parser.parse_args(
"-c config_file.json -n 2 --notify_init --incremental_sync --sort_keywords"
" --disable_autoupdate --debug_log".split()
" --disable_autoupdate --allow_conda_autoupdate --debug_log".split()
)
assert args.config == "config_file.json"
assert args.nthreads == 2
assert args.notify_init
assert args.incremental_sync
assert args.sort_keywords
assert args.disable_autoupdate
assert args.allow_conda_autoupdate
assert args.debug_log


Expand Down Expand Up @@ -83,14 +84,14 @@ def test_command_line_code_actions_options():
assert args.enable_code_actions


def unittest_server_init():
def unittest_server_init(conn=None):
from fortls.langserver import LangServer

root = (Path(__file__).parent / "test_source").resolve()
parser = cli("fortls")
args = parser.parse_args("-c f90_config.json".split())

server = LangServer(None, vars(args))
server = LangServer(conn, vars(args))
server.root_path = root
server._load_config_file()

Expand All @@ -104,6 +105,7 @@ def test_config_file_general_options():
assert server.incremental_sync
assert server.sort_keywords
assert server.disable_autoupdate
assert server.allow_conda_autoupdate


def test_config_file_dir_parsing_options():
Expand Down Expand Up @@ -164,28 +166,23 @@ def test_config_file_codeactions_options():
assert server.enable_code_actions


def test_version_update_pypi():
def test_version_update():
from packaging import version

from fortls.jsonrpc import JSONRPC2Connection, ReadWriter
from fortls.langserver import LangServer

parser = cli("fortls")
args = parser.parse_args("-c f90_config.json".split())
args = vars(args)
args["disable_autoupdate"] = False

stdin, stdout = sys.stdin.buffer, sys.stdout.buffer
s = LangServer(conn=JSONRPC2Connection(ReadWriter(stdin, stdout)), settings=args)
s.root_path = (Path(__file__).parent / "test_source").resolve()
did_update = s._update_version_pypi(test=True)
s, root = unittest_server_init(JSONRPC2Connection(ReadWriter(stdin, stdout)))
s.disable_autoupdate = False

did_update = s._update_version(test=True)
assert did_update

s.disable_autoupdate = True
did_update = s._update_version_pypi()
did_update = s._update_version()
assert not did_update

s.disable_autoupdate = False
s._version = version.parse("999.0.0")
did_update = s._update_version_pypi()
did_update = s._update_version()
assert not did_update
1 change: 1 addition & 0 deletions test/test_source/f90_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"incremental_sync": true,
"sort_keywords": true,
"disable_autoupdate": true,
"allow_conda_autoupdate": true,

"source_dirs": ["subdir", "pp/**"],
"incl_suffixes": [".FF", ".fpc", ".h", "f20"],
Expand Down