processes.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. """Process operations."""
  2. from __future__ import annotations
  3. import contextlib
  4. import os
  5. import signal
  6. import sys
  7. from typing import Optional
  8. from urllib.parse import urlparse
  9. import psutil
  10. from pynecone import constants
  11. from pynecone.config import get_config
  12. from pynecone.utils import console, prerequisites
  13. def kill(pid):
  14. """Kill a process.
  15. Args:
  16. pid: The process ID.
  17. """
  18. os.kill(pid, signal.SIGTERM)
  19. def get_num_workers() -> int:
  20. """Get the number of backend worker processes.
  21. Returns:
  22. The number of backend worker processes.
  23. """
  24. return 1 if prerequisites.get_redis() is None else (os.cpu_count() or 1) * 2 + 1
  25. def get_api_port() -> int:
  26. """Get the API port.
  27. Returns:
  28. The API port.
  29. """
  30. port = urlparse(get_config().api_url).port
  31. if port is None:
  32. port = urlparse(constants.API_URL).port
  33. assert port is not None
  34. return port
  35. def get_process_on_port(port) -> Optional[psutil.Process]:
  36. """Get the process on the given port.
  37. Args:
  38. port: The port.
  39. Returns:
  40. The process on the given port.
  41. """
  42. for proc in psutil.process_iter(["pid", "name", "cmdline"]):
  43. try:
  44. for conns in proc.connections(kind="inet"):
  45. if conns.laddr.port == int(port):
  46. return proc
  47. except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
  48. pass
  49. return None
  50. def is_process_on_port(port) -> bool:
  51. """Check if a process is running on the given port.
  52. Args:
  53. port: The port.
  54. Returns:
  55. Whether a process is running on the given port.
  56. """
  57. return get_process_on_port(port) is not None
  58. def kill_process_on_port(port):
  59. """Kill the process on the given port.
  60. Args:
  61. port: The port.
  62. """
  63. if get_process_on_port(port) is not None:
  64. with contextlib.suppress(psutil.AccessDenied):
  65. get_process_on_port(port).kill() # type: ignore
  66. def change_or_terminate_port(port, _type) -> str:
  67. """Terminate or change the port.
  68. Args:
  69. port: The port.
  70. _type: The type of the port.
  71. Returns:
  72. The new port or the current one.
  73. """
  74. console.print(
  75. f"Something is already running on port [bold underline]{port}[/bold underline]. This is the port the {_type} runs on."
  76. )
  77. frontend_action = console.ask("Kill or change it?", choices=["k", "c", "n"])
  78. if frontend_action == "k":
  79. kill_process_on_port(port)
  80. return port
  81. elif frontend_action == "c":
  82. new_port = console.ask("Specify the new port")
  83. # Check if also the new port is used
  84. if is_process_on_port(new_port):
  85. return change_or_terminate_port(new_port, _type)
  86. else:
  87. console.print(
  88. f"The {_type} will run on port [bold underline]{new_port}[/bold underline]."
  89. )
  90. return new_port
  91. else:
  92. console.print("Exiting...")
  93. sys.exit()