Skip to content

Allow conda autoupdate #346

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

Closed
wants to merge 4 commits into from
Closed
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
@@ -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": ["./**"],
8 changes: 7 additions & 1 deletion fortls/fortls.schema.json
Original file line number Diff line number Diff line change
@@ -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"
},
11 changes: 10 additions & 1 deletion fortls/interface.py
Original file line number Diff line number Diff line change
@@ -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
105 changes: 77 additions & 28 deletions fortls/langserver.py
Original file line number Diff line number Diff line change
@@ -202,7 +202,7 @@ def serve_initialize(self, request: dict):
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,
@@ -1590,6 +1590,9 @@ def _load_config_file_general(self, config_dict: dict) -> None:
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(
@@ -1760,8 +1763,8 @@ def _create_ref_link(self, obj) -> dict:
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
----------
@@ -1778,7 +1781,8 @@ def _update_version_pypi(self, test: bool = False):
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
@@ -1788,36 +1792,81 @@ def _update_version_pypi(self, test: bool = False):
"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)
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

self.post_message(
f"Downloading from conda-forge fortls {version}",
Severity.info,
)
# Run conda
result = subprocess.run(
[
"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


class JSONRPC2Error(Exception):
def __init__(self, code, message, data=None):
27 changes: 12 additions & 15 deletions test/test_interface.py
Original file line number Diff line number Diff line change
@@ -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


@@ -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()

@@ -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():
@@ -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
@@ -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"],