|
5 | 5 | import logging
|
6 | 6 | import os
|
7 | 7 | import re
|
| 8 | +import subprocess |
| 9 | +import sys |
8 | 10 | import traceback
|
| 11 | +import urllib.request |
9 | 12 | from multiprocessing import Pool
|
10 | 13 | from pathlib import Path
|
11 | 14 | from typing import Pattern
|
| 15 | +from urllib.error import URLError |
| 16 | + |
| 17 | +from packaging import version |
12 | 18 |
|
13 | 19 | # Local modules
|
14 | 20 | from fortls.constants import (
|
@@ -208,6 +214,8 @@ def serve_initialize(self, request):
|
208 | 214 | self._config_logger(request)
|
209 | 215 | self._load_intrinsics()
|
210 | 216 | self._add_source_dirs()
|
| 217 | + if self._update_version_pypi(): |
| 218 | + log.log("Please restart the server for new version to activate") |
211 | 219 |
|
212 | 220 | # Initialize workspace
|
213 | 221 | self.workspace_init()
|
@@ -1486,6 +1494,9 @@ def _load_config_file_general(self, config_dict: dict) -> None:
|
1486 | 1494 | )
|
1487 | 1495 | self.sync_type: int = 2 if self.incremental_sync else 1
|
1488 | 1496 | self.sort_keywords = config_dict.get("sort_keywords", self.sort_keywords)
|
| 1497 | + self.disable_autoupdate = config_dict.get( |
| 1498 | + "disable_autoupdate", self.disable_autoupdate |
| 1499 | + ) |
1489 | 1500 |
|
1490 | 1501 | # Autocomplete options -------------------------------------------------
|
1491 | 1502 | self.autocomplete_no_prefix = config_dict.get(
|
@@ -1634,6 +1645,55 @@ def _create_ref_link(self, obj):
|
1634 | 1645 | },
|
1635 | 1646 | }
|
1636 | 1647 |
|
| 1648 | + def _update_version_pypi(self, test: bool = False): |
| 1649 | + """Fetch updates from PyPi for fortls |
| 1650 | +
|
| 1651 | + Parameters |
| 1652 | + ---------- |
| 1653 | + test : bool, optional |
| 1654 | + flag used to override exit checks, only for unittesting, by default False |
| 1655 | + """ |
| 1656 | + if self.disable_autoupdate: |
| 1657 | + return False |
| 1658 | + v = version.parse(__version__) |
| 1659 | + # Do not run for prerelease and dev release |
| 1660 | + if v.is_prerelease and not test: |
| 1661 | + return False |
| 1662 | + try: |
| 1663 | + # For security reasons register as Request before opening |
| 1664 | + request = urllib.request.Request("https://pypi.org/pypi/fortls/json") |
| 1665 | + with urllib.request.urlopen(request) as resp: |
| 1666 | + info = json.loads(resp.read().decode("utf-8")) |
| 1667 | + # This is the only reliable way to compare version semantics |
| 1668 | + if version.parse(info["info"]["version"]) > v or test: |
| 1669 | + self.post_message( |
| 1670 | + f"Using fortls {__version__}. A newer version of is" |
| 1671 | + " available through PyPi. An attempt will be made to update" |
| 1672 | + " the server", |
| 1673 | + 3, |
| 1674 | + ) |
| 1675 | + # Run pip |
| 1676 | + result = subprocess.run( |
| 1677 | + [ |
| 1678 | + sys.executable, |
| 1679 | + "-m", |
| 1680 | + "pip", |
| 1681 | + "install", |
| 1682 | + "fortls", |
| 1683 | + "--upgrade", |
| 1684 | + ], |
| 1685 | + capture_output=True, |
| 1686 | + ) |
| 1687 | + if result.stdout: |
| 1688 | + log.info(result.stdout) |
| 1689 | + if result.stderr: |
| 1690 | + log.error(result.stderr) |
| 1691 | + return True |
| 1692 | + # No internet connection exceptions |
| 1693 | + except (URLError, KeyError): |
| 1694 | + log.warning("Failed to update the fortls Language Server") |
| 1695 | + return False |
| 1696 | + |
1637 | 1697 |
|
1638 | 1698 | class JSONRPC2Error(Exception):
|
1639 | 1699 | def __init__(self, code, message, data=None):
|
|
0 commit comments