rsconnect-python 1.33.1__tar.gz → 1.33.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/PKG-INFO +1 -1
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/pyproject.toml +1 -1
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/pyproject.toml.orig +1 -1
- rsconnect_python-1.33.2/rsconnect/version_check.py +169 -0
- rsconnect_python-1.33.1/rsconnect/version_check.py +0 -154
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/LICENSE.md +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/README.md +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/__init__.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/actions.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/actions_content.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/actions_environment.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/actions_integration.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/api.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/bundle.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/certificates.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/connect_cloud.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/device_login.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/environment.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/environment_node.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/environment_r.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/exception.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/git_metadata.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/http_support.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/json_web_token.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/log.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/main.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/metadata.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/models.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/oauth.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/preflight.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/preflight_node.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/py.typed +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/pyproject.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/__init__.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/quickstart.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/__init__.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/__connect__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/__init__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/__main__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/app.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/__connect__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/__init__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/__main__.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/app.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/fastapi/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/notebook/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/notebook/notebook.ipynb.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/notebook/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/quarto/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/quarto/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/quarto/report.qmd.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/shiny/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/shiny/app.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/shiny/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/streamlit/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/streamlit/app.py.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/streamlit/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/voila/README.md.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/voila/pyproject.toml.tmpl +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/shiny_express.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/snowflake.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/subprocesses/__init__.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/subprocesses/inspect_environment.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/timeouts.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/utils_package.py +0 -0
- {rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/validation.py +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name = "rsconnect_python"
|
|
3
3
|
description = "The Posit Connect command-line interface."
|
|
4
4
|
requires-python = ">=3.8"
|
|
5
|
-
version = "1.33.
|
|
5
|
+
version = "1.33.2"
|
|
6
6
|
dependencies = [ "typing-extensions>=4.8.0", "pip>=20.3", "uv>=0.9.0", "semver>=2.0.0,<4.0.0", "pyjwt>=2.4.0; python_version < '3.9'", "pyjwt>=2.15.0; python_version >= '3.9'", "click>=8.0.0", "packaging>=20.0", "toml>=0.10; python_version < '3.11'",]
|
|
7
7
|
[[project.authors]]
|
|
8
8
|
name = "Posit, PBC"
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Background version update check against PyPI.
|
|
2
|
+
|
|
3
|
+
Checked only on deploy commands. The latest known version is cached on disk and
|
|
4
|
+
refreshed at most once per :data:`_CACHE_TTL_SECONDS` in a background thread, so a
|
|
5
|
+
warm cache adds no network traffic and no latency and prints on every exit path
|
|
6
|
+
(including fast failures). Only a cold cache -- the first run, or an ephemeral
|
|
7
|
+
environment where the cache never persists -- waits briefly on the fetch so it can
|
|
8
|
+
still warn.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import platform
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
from http.client import HTTPSConnection
|
|
20
|
+
from os.path import join
|
|
21
|
+
from typing import NamedTuple, Optional, Tuple
|
|
22
|
+
|
|
23
|
+
from packaging.specifiers import InvalidSpecifier, SpecifierSet
|
|
24
|
+
from packaging.version import Version
|
|
25
|
+
|
|
26
|
+
from . import VERSION
|
|
27
|
+
from .metadata import config_dirname, makedirs
|
|
28
|
+
|
|
29
|
+
RSCONNECT_DISABLE_VERSION_CHECK = "RSCONNECT_DISABLE_VERSION_CHECK"
|
|
30
|
+
_PYPI_HOST = "pypi.org"
|
|
31
|
+
_PYPI_PATH = "/pypi/rsconnect-python/json"
|
|
32
|
+
_PYPI_TIMEOUT_SECONDS = 2
|
|
33
|
+
_CACHE_TTL_SECONDS = 24 * 60 * 60
|
|
34
|
+
_CACHE_FILENAME = "version_check.json"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _Release(NamedTuple):
|
|
38
|
+
version: str
|
|
39
|
+
requires_python: Optional[str]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _is_check_disabled() -> bool:
|
|
43
|
+
value = os.environ.get(RSCONNECT_DISABLE_VERSION_CHECK, "").strip().lower()
|
|
44
|
+
return value in ("1", "true", "yes")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _is_dev_version(version_str: str) -> bool:
|
|
48
|
+
try:
|
|
49
|
+
return Version(version_str).is_devrelease
|
|
50
|
+
except Exception:
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _cache_path() -> str:
|
|
55
|
+
return join(config_dirname(), _CACHE_FILENAME)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _read_cache() -> Tuple[bool, Optional[_Release]]:
|
|
59
|
+
"""Return freshness and the cached release, retaining stale data for the hint."""
|
|
60
|
+
try:
|
|
61
|
+
with open(_cache_path()) as f:
|
|
62
|
+
data = json.load(f)
|
|
63
|
+
latest = data.get("latest")
|
|
64
|
+
requires_python = data.get("requires_python")
|
|
65
|
+
release = (
|
|
66
|
+
_Release(latest, requires_python if isinstance(requires_python, str) else None)
|
|
67
|
+
if isinstance(latest, str)
|
|
68
|
+
else None
|
|
69
|
+
)
|
|
70
|
+
is_fresh = "requires_python" in data and time.time() - float(data["checked_at"]) <= _CACHE_TTL_SECONDS
|
|
71
|
+
return (is_fresh, release)
|
|
72
|
+
except Exception:
|
|
73
|
+
return (False, None)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _write_cache(release: Optional[_Release]) -> None:
|
|
77
|
+
"""Cache failures too, so they do not trigger another request until the TTL expires."""
|
|
78
|
+
try:
|
|
79
|
+
path = _cache_path()
|
|
80
|
+
makedirs(path)
|
|
81
|
+
with open(path, "w") as f:
|
|
82
|
+
json.dump(
|
|
83
|
+
{
|
|
84
|
+
"checked_at": time.time(),
|
|
85
|
+
"latest": release.version if release else None,
|
|
86
|
+
"requires_python": release.requires_python if release else None,
|
|
87
|
+
},
|
|
88
|
+
f,
|
|
89
|
+
)
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _fetch_latest_version() -> Optional[_Release]:
|
|
95
|
+
conn = None
|
|
96
|
+
try:
|
|
97
|
+
conn = HTTPSConnection(_PYPI_HOST, timeout=_PYPI_TIMEOUT_SECONDS)
|
|
98
|
+
conn.request("GET", _PYPI_PATH, headers={"Accept": "application/json"})
|
|
99
|
+
response = conn.getresponse()
|
|
100
|
+
if response.status != 200:
|
|
101
|
+
return None
|
|
102
|
+
info = json.loads(response.read()).get("info", {})
|
|
103
|
+
latest = info.get("version")
|
|
104
|
+
requires_python = info.get("requires_python")
|
|
105
|
+
if isinstance(latest, str):
|
|
106
|
+
return _Release(latest, requires_python if isinstance(requires_python, str) else None)
|
|
107
|
+
return None
|
|
108
|
+
except Exception:
|
|
109
|
+
return None
|
|
110
|
+
finally:
|
|
111
|
+
if conn is not None:
|
|
112
|
+
conn.close()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _update_message(release: Optional[_Release]) -> Optional[str]:
|
|
116
|
+
"""Return an upgrade warning if the release is newer than the running version."""
|
|
117
|
+
try:
|
|
118
|
+
if release is None or Version(release.version) <= Version(VERSION):
|
|
119
|
+
return None
|
|
120
|
+
python_version = platform.python_version()
|
|
121
|
+
try:
|
|
122
|
+
compatible = not release.requires_python or SpecifierSet(release.requires_python).contains(
|
|
123
|
+
python_version, prereleases=True
|
|
124
|
+
)
|
|
125
|
+
except InvalidSpecifier:
|
|
126
|
+
compatible = True
|
|
127
|
+
if not compatible:
|
|
128
|
+
force = "" if os.path.isfile(join(sys.prefix, "uv-receipt.toml")) else "--force "
|
|
129
|
+
return (
|
|
130
|
+
f"The latest version of rsconnect-python ({release.version}) requires Python "
|
|
131
|
+
f"{release.requires_python}, but you are running Python {python_version}.\n"
|
|
132
|
+
f'Install it with: uv tool install {force}--python "{release.requires_python}" rsconnect-python'
|
|
133
|
+
)
|
|
134
|
+
return (
|
|
135
|
+
f"A new version of rsconnect-python is available: {release.version} "
|
|
136
|
+
f"(you have {VERSION}).\n"
|
|
137
|
+
"Upgrade with: pip install --upgrade rsconnect-python"
|
|
138
|
+
)
|
|
139
|
+
except Exception:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class BackgroundVersionCheck:
|
|
144
|
+
"""Resolves the latest available version, using a cache and a background fetch."""
|
|
145
|
+
|
|
146
|
+
def __init__(self) -> None:
|
|
147
|
+
self._thread: Optional[threading.Thread] = None
|
|
148
|
+
self._release: Optional[_Release] = None
|
|
149
|
+
|
|
150
|
+
def start(self) -> None:
|
|
151
|
+
if _is_check_disabled() or _is_dev_version(VERSION):
|
|
152
|
+
return
|
|
153
|
+
is_fresh, self._release = _read_cache()
|
|
154
|
+
if not is_fresh:
|
|
155
|
+
self._thread = threading.Thread(target=self._run, daemon=True)
|
|
156
|
+
self._thread.start()
|
|
157
|
+
|
|
158
|
+
def _run(self) -> None:
|
|
159
|
+
release = _fetch_latest_version()
|
|
160
|
+
_write_cache(release)
|
|
161
|
+
# Keep a usable stale value when a refresh fails.
|
|
162
|
+
if release is not None:
|
|
163
|
+
self._release = release
|
|
164
|
+
|
|
165
|
+
def get_warning_message(self) -> Optional[str]:
|
|
166
|
+
# Only a cold cache waits briefly for the background fetch.
|
|
167
|
+
if self._release is None and self._thread is not None:
|
|
168
|
+
self._thread.join(timeout=_PYPI_TIMEOUT_SECONDS)
|
|
169
|
+
return _update_message(self._release)
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
"""Background version update check against PyPI.
|
|
2
|
-
|
|
3
|
-
Checked only on deploy commands. The latest known version is cached on disk and
|
|
4
|
-
refreshed at most once per :data:`_CACHE_TTL_SECONDS` in a background thread, so a
|
|
5
|
-
warm cache adds no network traffic and no latency and prints on every exit path
|
|
6
|
-
(including fast failures). Only a cold cache -- the first run, or an ephemeral
|
|
7
|
-
environment where the cache never persists -- waits briefly on the fetch so it can
|
|
8
|
-
still warn.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
|
-
import json
|
|
14
|
-
import os
|
|
15
|
-
import threading
|
|
16
|
-
import time
|
|
17
|
-
from http.client import HTTPSConnection
|
|
18
|
-
from os.path import join
|
|
19
|
-
from typing import Optional, Tuple
|
|
20
|
-
|
|
21
|
-
from packaging.version import Version
|
|
22
|
-
|
|
23
|
-
from . import VERSION
|
|
24
|
-
from .metadata import config_dirname, makedirs
|
|
25
|
-
|
|
26
|
-
RSCONNECT_DISABLE_VERSION_CHECK = "RSCONNECT_DISABLE_VERSION_CHECK"
|
|
27
|
-
_PYPI_HOST = "pypi.org"
|
|
28
|
-
_PYPI_PATH = "/pypi/rsconnect-python/json"
|
|
29
|
-
_PYPI_TIMEOUT_SECONDS = 2
|
|
30
|
-
_CACHE_TTL_SECONDS = 24 * 60 * 60 # Re-check PyPI at most once a day.
|
|
31
|
-
_CACHE_FILENAME = "version_check.json"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def _is_check_disabled() -> bool:
|
|
35
|
-
value = os.environ.get(RSCONNECT_DISABLE_VERSION_CHECK, "").strip().lower()
|
|
36
|
-
return value in ("1", "true", "yes")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _is_dev_version(version_str: str) -> bool:
|
|
40
|
-
try:
|
|
41
|
-
return Version(version_str).is_devrelease
|
|
42
|
-
except Exception:
|
|
43
|
-
return True
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _cache_path() -> str:
|
|
47
|
-
return join(config_dirname(), _CACHE_FILENAME)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def _read_cache() -> Tuple[bool, Optional[str]]:
|
|
51
|
-
"""Return ``(is_fresh, latest)``.
|
|
52
|
-
|
|
53
|
-
``latest`` is the last known PyPI version recorded in the cache (or None when
|
|
54
|
-
the cache is missing/unreadable or the last fetch failed). It is returned
|
|
55
|
-
regardless of freshness so even a stale value can drive the upgrade hint while
|
|
56
|
-
a refresh runs in the background. ``is_fresh`` is True only when the entry is
|
|
57
|
-
within the TTL, signalling that no background refresh is needed.
|
|
58
|
-
"""
|
|
59
|
-
try:
|
|
60
|
-
with open(_cache_path()) as f:
|
|
61
|
-
data = json.load(f)
|
|
62
|
-
latest = data.get("latest")
|
|
63
|
-
latest = latest if isinstance(latest, str) else None
|
|
64
|
-
is_fresh = time.time() - float(data["checked_at"]) <= _CACHE_TTL_SECONDS
|
|
65
|
-
return (is_fresh, latest)
|
|
66
|
-
except Exception:
|
|
67
|
-
return (False, None)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def _write_cache(latest: Optional[str]) -> None:
|
|
71
|
-
"""Persist the latest known version (or None) with the current timestamp.
|
|
72
|
-
|
|
73
|
-
A None value is still cached so repeated failures don't re-hit PyPI on every
|
|
74
|
-
deploy until the TTL expires.
|
|
75
|
-
"""
|
|
76
|
-
try:
|
|
77
|
-
path = _cache_path()
|
|
78
|
-
makedirs(path)
|
|
79
|
-
with open(path, "w") as f:
|
|
80
|
-
json.dump({"checked_at": time.time(), "latest": latest}, f)
|
|
81
|
-
except Exception:
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _fetch_latest_version() -> Optional[str]:
|
|
86
|
-
conn = None
|
|
87
|
-
try:
|
|
88
|
-
conn = HTTPSConnection(_PYPI_HOST, timeout=_PYPI_TIMEOUT_SECONDS)
|
|
89
|
-
conn.request("GET", _PYPI_PATH, headers={"Accept": "application/json"})
|
|
90
|
-
response = conn.getresponse()
|
|
91
|
-
if response.status != 200:
|
|
92
|
-
return None
|
|
93
|
-
data = json.loads(response.read())
|
|
94
|
-
return data.get("info", {}).get("version")
|
|
95
|
-
except Exception:
|
|
96
|
-
return None
|
|
97
|
-
finally:
|
|
98
|
-
if conn is not None:
|
|
99
|
-
conn.close()
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def _update_message(latest: Optional[str]) -> Optional[str]:
|
|
103
|
-
"""Return an upgrade warning if ``latest`` is newer than the running version."""
|
|
104
|
-
try:
|
|
105
|
-
if latest is not None and Version(latest) > Version(VERSION):
|
|
106
|
-
return (
|
|
107
|
-
f"A new version of rsconnect-python is available: {latest} "
|
|
108
|
-
f"(you have {VERSION}).\n"
|
|
109
|
-
f"Upgrade with: pip install --upgrade rsconnect-python"
|
|
110
|
-
)
|
|
111
|
-
except Exception:
|
|
112
|
-
pass
|
|
113
|
-
return None
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class BackgroundVersionCheck:
|
|
117
|
-
"""Resolves the latest available version, using a cache and a background fetch."""
|
|
118
|
-
|
|
119
|
-
def __init__(self) -> None:
|
|
120
|
-
self._thread: Optional[threading.Thread] = None
|
|
121
|
-
self._latest: Optional[str] = None
|
|
122
|
-
|
|
123
|
-
def start(self) -> None:
|
|
124
|
-
if _is_check_disabled() or _is_dev_version(VERSION):
|
|
125
|
-
return
|
|
126
|
-
is_fresh, latest = _read_cache()
|
|
127
|
-
# Seed the hint from the cached value (even a stale one is good enough to
|
|
128
|
-
# suggest an upgrade), so a warm cache prints instantly on every exit path
|
|
129
|
-
# -- including commands that fail fast.
|
|
130
|
-
self._latest = latest
|
|
131
|
-
if not is_fresh:
|
|
132
|
-
# Refresh in the background so the fetch overlaps the command's own
|
|
133
|
-
# work. A warm or stale cache never waits on this; only a cold cache
|
|
134
|
-
# waits briefly at exit -- see get_warning_message.
|
|
135
|
-
self._thread = threading.Thread(target=self._run, daemon=True)
|
|
136
|
-
self._thread.start()
|
|
137
|
-
|
|
138
|
-
def _run(self) -> None:
|
|
139
|
-
latest = _fetch_latest_version()
|
|
140
|
-
_write_cache(latest)
|
|
141
|
-
# Adopt a successful result so a cold-cache run can warn this time, but
|
|
142
|
-
# never overwrite a usable cached value with a failed (None) refresh.
|
|
143
|
-
if latest is not None:
|
|
144
|
-
self._latest = latest
|
|
145
|
-
|
|
146
|
-
def get_warning_message(self) -> Optional[str]:
|
|
147
|
-
# With no cached value yet (a cold cache: first run, or an ephemeral
|
|
148
|
-
# environment where the cache never persists), give the in-flight fetch a
|
|
149
|
-
# bounded chance to finish so this run can still warn. The bound is the
|
|
150
|
-
# fetch's own time budget -- a slower network just means no hint this run.
|
|
151
|
-
# A warm or stale cache already has a value here and never waits.
|
|
152
|
-
if self._latest is None and self._thread is not None:
|
|
153
|
-
self._thread.join(timeout=_PYPI_TIMEOUT_SECONDS)
|
|
154
|
-
return _update_message(self._latest)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/api/app.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/quickstart/templates/shiny/app.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rsconnect_python-1.33.1 → rsconnect_python-1.33.2}/rsconnect/subprocesses/inspect_environment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|