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

Added fortls autoupdate during initialisation #75

Merged
merged 4 commits into from
Mar 8, 2022
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELONG

## 2.2.6

### Added

- Added the capability for `fortls` to auto-update use `--disable_autoupdate` to disable
([#76](https://github.com/gnikit/fortls/issues/76))

## 2.2.5

### Changed
Expand Down
8 changes: 8 additions & 0 deletions fortls/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ def commandline_args(name: str = "fortls") -> argparse.ArgumentParser:
" as is)"
),
)
parser.add_argument(
"--disable_autoupdate",
action="store_true",
help=(
"fortls automatically checks PyPi for newer version and installs them."
"Use this option to disable the autoupdate feature."
),
)
# XXX: Deprecated, argument not attached to anything. Remove
parser.add_argument(
"--preserve_keyword_order",
Expand Down
60 changes: 60 additions & 0 deletions fortls/langserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
import logging
import os
import re
import subprocess
import sys
import traceback
import urllib.request
from multiprocessing import Pool
from pathlib import Path
from typing import Pattern
from urllib.error import URLError

from packaging import version

# Local modules
from fortls.constants import (
Expand Down Expand Up @@ -208,6 +214,8 @@ def serve_initialize(self, request):
self._config_logger(request)
self._load_intrinsics()
self._add_source_dirs()
if self._update_version_pypi():
log.log("Please restart the server for new version to activate")

# Initialize workspace
self.workspace_init()
Expand Down Expand Up @@ -1486,6 +1494,9 @@ def _load_config_file_general(self, config_dict: dict) -> None:
)
self.sync_type: int = 2 if self.incremental_sync else 1
self.sort_keywords = config_dict.get("sort_keywords", self.sort_keywords)
self.disable_autoupdate = config_dict.get(
"disable_autoupdate", self.disable_autoupdate
)

# Autocomplete options -------------------------------------------------
self.autocomplete_no_prefix = config_dict.get(
Expand Down Expand Up @@ -1634,6 +1645,55 @@ def _create_ref_link(self, obj):
},
}

def _update_version_pypi(self, test: bool = False):
"""Fetch updates from PyPi for fortls

Parameters
----------
test : bool, optional
flag used to override exit checks, only for unittesting, by default False
"""
if self.disable_autoupdate:
return False
v = version.parse(__version__)
# Do not run for prerelease and dev release
if v.is_prerelease and not test:
return False
try:
# For security reasons register as Request before opening
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"))
# This is the only reliable way to compare version semantics
if version.parse(info["info"]["version"]) > v or test:
self.post_message(
f"Using fortls {__version__}. A newer version of is"
" available through PyPi. An attempt will be made to update"
" the server",
3,
)
# Run pip
result = subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"fortls",
"--upgrade",
],
capture_output=True,
)
if result.stdout:
log.info(result.stdout)
if result.stderr:
log.error(result.stderr)
return True
# No internet connection exceptions
except (URLError, KeyError):
log.warning("Failed to update the fortls Language Server")
return False


class JSONRPC2Error(Exception):
def __init__(self, code, message, data=None):
Expand Down
2 changes: 1 addition & 1 deletion fortls/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
VAR_TYPE_ID,
WHERE_TYPE_ID,
FRegex,
USE_info,
INCLUDE_info,
USE_info,
)
from fortls.helper_functions import get_keywords, get_paren_substring, get_var_stack
from fortls.jsonrpc import path_to_uri
Expand Down
4 changes: 2 additions & 2 deletions fortls/parse_fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
PY3K,
SELECT_TYPE_ID,
SUBMODULE_TYPE_ID,
CLASS_info,
FRegex,
FUN_sig,
RESULT_sig,
CLASS_info,
GEN_info,
INT_info,
RESULT_sig,
SELECT_info,
SMOD_info,
SUB_info,
Expand Down
20 changes: 19 additions & 1 deletion test/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
def test_command_line_general_options():
args = parser.parse_args(
"-c config_file.json -n 2 --notify_init --incremental_sync --sort_keywords"
" --debug_log".split()
" --disable_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.debug_log


Expand Down Expand Up @@ -103,6 +104,7 @@ def test_config_file_general_options():
assert server.notify_init
assert server.incremental_sync
assert server.sort_keywords
assert server.disable_autoupdate


def test_config_file_dir_parsing_options():
Expand Down Expand Up @@ -164,3 +166,19 @@ def test_config_file_codeactions_options():
server, root = unittest_server_init()
# Code Actions options
assert server.enable_code_actions


def test_version_update_pypi():
from fortls.langserver import LangServer
from fortls.jsonrpc import JSONRPC2Connection, ReadWriter

parser = commandline_args("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)
assert 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 @@ -3,6 +3,7 @@
"notify_init": true,
"incremental_sync": true,
"sort_keywords": true,
"disable_autoupdate": true,

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