[REF-2676][REF-2751] Windows Skip ARM devices on bun install + Telemetry (#3212)

This commit is contained in:
Elijah Ahianyo 2024-05-06 09:50:05 -07:00 committed by GitHub
parent 9c7dbdbc72
commit 0838e5ac6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 154 additions and 12 deletions

View File

@ -4,7 +4,6 @@ from .base import (
COOKIES, COOKIES,
ENV_MODE_ENV_VAR, ENV_MODE_ENV_VAR,
IS_WINDOWS, IS_WINDOWS,
IS_WINDOWS_BUN_SUPPORTED_MACHINE, # type: ignore
LOCAL_STORAGE, LOCAL_STORAGE,
POLLING_MAX_HTTP_BUFFER_SIZE, POLLING_MAX_HTTP_BUFFER_SIZE,
PYTEST_CURRENT_TEST, PYTEST_CURRENT_TEST,
@ -87,7 +86,6 @@ __ALL__ = [
Hooks, Hooks,
Imports, Imports,
IS_WINDOWS, IS_WINDOWS,
IS_WINDOWS_BUN_SUPPORTED_MACHINE,
LOCAL_STORAGE, LOCAL_STORAGE,
LogLevel, LogLevel,
MemoizationDisposition, MemoizationDisposition,

View File

@ -11,11 +11,6 @@ from types import SimpleNamespace
from platformdirs import PlatformDirs from platformdirs import PlatformDirs
IS_WINDOWS = platform.system() == "Windows" IS_WINDOWS = platform.system() == "Windows"
# https://github.com/oven-sh/bun/blob/main/src/cli/install.ps1
IS_WINDOWS_BUN_SUPPORTED_MACHINE = IS_WINDOWS and platform.machine() in [
"AMD64",
"x86_64",
] # filter out 32 bit + ARM
class Dirs(SimpleNamespace): class Dirs(SimpleNamespace):

View File

@ -280,7 +280,11 @@ def output_system_info():
system = platform.system() system = platform.system()
if system != "Windows": if (
system != "Windows"
or system == "Windows"
and prerequisites.is_windows_bun_supported()
):
dependencies.extend( dependencies.extend(
[ [
f"[FNM {prerequisites.get_fnm_version()} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]", f"[FNM {prerequisites.get_fnm_version()} (Expected: {constants.Fnm.VERSION}) (PATH: {constants.Fnm.EXE})]",

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import functools
import glob import glob
import importlib import importlib
import inspect import inspect
@ -49,6 +50,14 @@ class Template(Base):
demo_url: str demo_url: str
class CpuInfo(Base):
"""Model to save cpu info."""
manufacturer_id: Optional[str]
model_name: Optional[str]
address_width: Optional[int]
def check_latest_package_version(package_name: str): def check_latest_package_version(package_name: str):
"""Check if the latest version of the package is installed. """Check if the latest version of the package is installed.
@ -172,7 +181,7 @@ def get_install_package_manager() -> str | None:
Returns: Returns:
The path to the package manager. The path to the package manager.
""" """
if constants.IS_WINDOWS and not constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE: if constants.IS_WINDOWS and not is_windows_bun_supported():
return get_package_manager() return get_package_manager()
return get_config().bun_path return get_config().bun_path
@ -728,7 +737,7 @@ def install_bun():
Raises: Raises:
FileNotFoundError: If required packages are not found. FileNotFoundError: If required packages are not found.
""" """
if constants.IS_WINDOWS and not constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE: if constants.IS_WINDOWS and not is_windows_bun_supported():
console.warn( console.warn(
"Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm." "Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
) )
@ -833,7 +842,7 @@ def install_frontend_packages(packages: set[str], config: Config):
get_package_manager() get_package_manager()
if not constants.IS_WINDOWS if not constants.IS_WINDOWS
or constants.IS_WINDOWS or constants.IS_WINDOWS
and constants.IS_WINDOWS_BUN_SUPPORTED_MACHINE and is_windows_bun_supported()
else None else None
) )
processes.run_process_with_fallback( processes.run_process_with_fallback(
@ -1418,3 +1427,92 @@ def initialize_app(app_name: str, template: str | None = None):
) )
telemetry.send("init", template=template) telemetry.send("init", template=template)
def format_address_width(address_width) -> int | None:
"""Cast address width to an int.
Args:
address_width: The address width.
Returns:
Address width int
"""
try:
return int(address_width) if address_width else None
except ValueError:
return None
@functools.lru_cache(maxsize=None)
def get_cpu_info() -> CpuInfo | None:
"""Get the CPU info of the underlining host.
Returns:
The CPU info.
"""
platform_os = platform.system()
cpuinfo = {}
try:
if platform_os == "Windows":
cmd = "wmic cpu get addresswidth,caption,manufacturer /FORMAT:csv"
output = processes.execute_command_and_return_output(cmd)
if output:
val = output.splitlines()[-1].split(",")[1:]
cpuinfo["manufacturer_id"] = val[2]
cpuinfo["model_name"] = val[1].split("Family")[0].strip()
cpuinfo["address_width"] = format_address_width(val[0])
elif platform_os == "Linux":
output = processes.execute_command_and_return_output("lscpu")
if output:
lines = output.split("\n")
for line in lines:
if "Architecture" in line:
cpuinfo["address_width"] = (
64 if line.split(":")[1].strip() == "x86_64" else 32
)
if "Vendor ID:" in line:
cpuinfo["manufacturer_id"] = line.split(":")[1].strip()
if "Model name" in line:
cpuinfo["model_name"] = line.split(":")[1].strip()
elif platform_os == "Darwin":
cpuinfo["address_width"] = format_address_width(
processes.execute_command_and_return_output("getconf LONG_BIT")
)
cpuinfo["manufacturer_id"] = processes.execute_command_and_return_output(
"sysctl -n machdep.cpu.brand_string"
)
cpuinfo["model_name"] = processes.execute_command_and_return_output(
"uname -m"
)
except Exception as err:
console.error(f"Failed to retrieve CPU info. {err}")
return None
return (
CpuInfo(
manufacturer_id=cpuinfo.get("manufacturer_id"),
model_name=cpuinfo.get("model_name"),
address_width=cpuinfo.get("address_width"),
)
if cpuinfo
else None
)
@functools.lru_cache(maxsize=None)
def is_windows_bun_supported() -> bool:
"""Check whether the underlining host running windows qualifies to run bun.
We typically do not run bun on ARM or 32 bit devices that use windows.
Returns:
Whether the host is qualified to use bun.
"""
cpu_info = get_cpu_info()
return (
constants.IS_WINDOWS
and cpu_info is not None
and cpu_info.address_width == 64
and cpu_info.model_name is not None
and "ARM" not in cpu_info.model_name
)

View File

@ -347,3 +347,21 @@ def run_process_with_fallback(args, *, show_status_message, fallback=None, **kwa
fallback=None, fallback=None,
**kwargs, **kwargs,
) )
def execute_command_and_return_output(command) -> str | None:
"""Execute a command and return the output.
Args:
command: The command to run.
Returns:
The output of the command.
"""
try:
return subprocess.check_output(command, shell=True).decode().strip()
except subprocess.SubprocessError as err:
console.error(
f"The command `{command}` failed with error: {err}. This will return None."
)
return None

View File

@ -32,6 +32,15 @@ def get_os() -> str:
return platform.system() return platform.system()
def get_detailed_platform_str() -> str:
"""Get the detailed os/platform string.
Returns:
The platform string
"""
return platform.platform()
def get_python_version() -> str: def get_python_version() -> str:
"""Get the Python version. """Get the Python version.
@ -97,6 +106,8 @@ def _prepare_event(event: str, **kwargs) -> dict:
Returns: Returns:
The event data. The event data.
""" """
from reflex.utils.prerequisites import get_cpu_info
installation_id = ensure_reflex_installation_id() installation_id = ensure_reflex_installation_id()
project_hash = get_project_hash(raise_on_fail=_raise_on_missing_project_hash()) project_hash = get_project_hash(raise_on_fail=_raise_on_missing_project_hash())
@ -112,6 +123,9 @@ def _prepare_event(event: str, **kwargs) -> dict:
else: else:
# for python 3.11 & 3.12 # for python 3.11 & 3.12
stamp = datetime.now(UTC).isoformat() stamp = datetime.now(UTC).isoformat()
cpuinfo = get_cpu_info()
return { return {
"api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb", "api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb",
"event": event, "event": event,
@ -119,10 +133,12 @@ def _prepare_event(event: str, **kwargs) -> dict:
"distinct_id": installation_id, "distinct_id": installation_id,
"distinct_app_id": project_hash, "distinct_app_id": project_hash,
"user_os": get_os(), "user_os": get_os(),
"user_os_detail": get_detailed_platform_str(),
"reflex_version": get_reflex_version(), "reflex_version": get_reflex_version(),
"python_version": get_python_version(), "python_version": get_python_version(),
"cpu_count": get_cpu_count(), "cpu_count": get_cpu_count(),
"memory": get_memory(), "memory": get_memory(),
"cpu_info": dict(cpuinfo) if cpuinfo else {},
**( **(
{"template": template} {"template": template}
if (template := kwargs.get("template")) is not None if (template := kwargs.get("template")) is not None
@ -165,5 +181,4 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs) -> bool:
event_data = _prepare_event(event, **kwargs) event_data = _prepare_event(event, **kwargs)
if not event_data: if not event_data:
return False return False
return _send_event(event_data) return _send_event(event_data)

View File

@ -8,8 +8,10 @@ import pytest
from reflex import constants from reflex import constants
from reflex.config import Config from reflex.config import Config
from reflex.utils.prerequisites import ( from reflex.utils.prerequisites import (
CpuInfo,
_update_next_config, _update_next_config,
cached_procedure, cached_procedure,
get_cpu_info,
initialize_requirements_txt, initialize_requirements_txt,
) )
@ -203,3 +205,14 @@ def test_cached_procedure():
assert call_count == 2 assert call_count == 2
_function_with_some_args(100, y=300) _function_with_some_args(100, y=300)
assert call_count == 2 assert call_count == 2
def test_get_cpu_info():
cpu_info = get_cpu_info()
assert cpu_info is not None
assert isinstance(cpu_info, CpuInfo)
assert cpu_info.model_name is not None
for attr in ("manufacturer_id", "model_name", "address_width"):
value = getattr(cpu_info, attr)
assert value.strip() if attr != "address_width" else value

View File

@ -41,6 +41,7 @@ def test_send(mocker, event):
read_data='{"project_hash": "78285505863498957834586115958872998605"}' read_data='{"project_hash": "78285505863498957834586115958872998605"}'
), ),
) )
mocker.patch("platform.platform", return_value="Mocked Platform")
telemetry.send(event, telemetry_enabled=True) telemetry.send(event, telemetry_enabled=True)
httpx.post.assert_called_once() httpx.post.assert_called_once()