diff --git a/poetry.lock b/poetry.lock index c402c571..d5c78373 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1849,6 +1849,25 @@ type = "legacy" url = "https://pypi.org/simple" reference = "pypi-public" +[[package]] +name = "pep8-naming" +version = "0.14.1" +description = "Check PEP-8 naming conventions, plugin for flake8" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pep8-naming-0.14.1.tar.gz", hash = "sha256:1ef228ae80875557eb6c1549deafed4dabbf3261cfcafa12f773fe0db9be8a36"}, + {file = "pep8_naming-0.14.1-py3-none-any.whl", hash = "sha256:63f514fc777d715f935faf185dedd679ab99526a7f2f503abb61587877f7b1c5"}, +] + +[package.dependencies] +flake8 = ">=5.0.0" + +[package.source] +type = "legacy" +url = "https://pypi.org/simple" +reference = "pypi-public" + [[package]] name = "pillow" version = "10.4.0" @@ -3673,4 +3692,4 @@ prefigure = ["prefig"] [metadata] lock-version = "2.0" python-versions = "^3.8.5" -content-hash = "48fff45f6ea88dd07c22a53b1511b902cc6210c8ef8b522ad89b4dae836cece3" +content-hash = "416adea3cbd5237abf57db2c909dfddc7d0a21cc9d854b7a931ff87c4f42af24" diff --git a/pretext/cli.py b/pretext/cli.py index 24a64be8..1ae9e041 100644 --- a/pretext/cli.py +++ b/pretext/cli.py @@ -20,7 +20,6 @@ from typing import Any, Callable, List, Literal, Optional from functools import update_wrapper - from . import ( utils, resources, @@ -32,7 +31,6 @@ CORE_COMMIT, ) - from .project import Project log = logging.getLogger("ptxlogger") @@ -58,6 +56,7 @@ # Add a decorator to provide nice exception handling for validation errors for all commands. It avoids printing a confusing traceback, and also nicely formats validation errors. def nice_errors(f: Callable[..., None]) -> Any: + @click.pass_context def try_except(ctx: click.Context, *args: Any, **kwargs: Any) -> Any: try: @@ -790,8 +789,8 @@ def view( if stop_server: try: - projectHash = utils.hash_path(project.abspath()) - current_server = server.active_server_for_path_hash(projectHash) + project_hash = utils.hash_path(project.abspath()) + current_server = server.active_server_for_path_hash(project_hash) log.info("\nStopping server.") if current_server: current_server.terminate() @@ -854,8 +853,8 @@ def view( webbrowser.open(url) return # Start server if there isn't one running already: - projectHash = utils.hash_path(project.abspath()) - current_server = server.active_server_for_path_hash(projectHash) + project_hash = utils.hash_path(project.abspath()) + current_server = server.active_server_for_path_hash(project_hash) if restart_server and current_server is not None: log.info( f"Terminating existing server {current_server.pid} on port {current_server.port}" @@ -863,7 +862,7 @@ def view( current_server.terminate() current_server = None # Double check that the current server really is active: - if current_server is not None and current_server.isActiveServer(): + if current_server is not None and current_server.is_active_server(): url_base = utils.url_for_access(access=access, port=current_server.port) url = url_base + url_path log.info(f"Server is already available at {url_base}") diff --git a/pretext/server.py b/pretext/server.py index 5e1fda0a..5bb115ec 100644 --- a/pretext/server.py +++ b/pretext/server.py @@ -13,27 +13,32 @@ # Get access to logger log = logging.getLogger("ptxlogger") +# Limit for how many entries to allow in the server file +# before attempting to clean up non-running entries. +# Note: This is not a limit to the number of concurrent servers. +PURGE_LIMIT = 10 + @dataclass class RunningServerInfo: """A simple dataclass to hold the information in the running servers file.""" - pathHash: str + path_hash: str pid: int port: int binding: str @staticmethod - def fromFileLine(line: str) -> RunningServerInfo: - (pathHash, pid, port, binding) = line.split() + def from_file_line(line: str) -> RunningServerInfo: + (path_hash, pid, port, binding) = line.split() return RunningServerInfo( - pathHash=pathHash, pid=int(pid), port=int(port), binding=binding + path_hash=path_hash, pid=int(pid), port=int(port), binding=binding ) - def toFileLine(self) -> str: - return f"{self.pathHash} {self.pid} {self.port} {self.binding}\n" + def to_file_line(self) -> str: + return f"{self.path_hash} {self.pid} {self.port} {self.binding}\n" - def isActiveServer(self) -> bool: + def is_active_server(self) -> bool: """Returns whether the server represented by this object is active on the provided port""" p = psutil.Process(self.pid) if not p.is_running(): @@ -56,7 +61,7 @@ def terminate(self) -> None: try: log.info(f"Terminating {self.pid}") psutil.Process(self.pid).terminate() - remove_server_entry(self.pathHash) + remove_server_entry(self.path_hash) except Exception as e: log.info(f"Terminate failed for {self.pid}.") log.exception(e, exc_info=True) @@ -73,18 +78,18 @@ def get_running_servers() -> t.List[RunningServerInfo]: if not home_path().exists(): return [] try: - runningServersFile = home_path() / "running_servers" - if not runningServersFile.is_file(): + running_servers_file = home_path() / "running_servers" + if not running_servers_file.is_file(): return [] - with open(runningServersFile, "r") as f: - return [RunningServerInfo.fromFileLine(line) for line in f.readlines()] + with open(running_servers_file, "r") as f: + return [RunningServerInfo.from_file_line(line) for line in f.readlines()] except IOError as e: log.info("Unable to open running servers file.") log.exception(e, exc_info=True) return [] -def save_running_servers(runningServers: t.List[RunningServerInfo]) -> None: +def save_running_servers(servers: t.List[RunningServerInfo]) -> None: """ Overwrites the ~/.ptx/running_servers file to store the new list of running servers. @@ -92,36 +97,37 @@ def save_running_servers(runningServers: t.List[RunningServerInfo]) -> None: # Ensure home path exists os.makedirs(home_path(), exist_ok=True) try: - runningServersFile = home_path() / "running_servers" - with open(runningServersFile, "w") as f: + running_servers_file = home_path() / "running_servers" + with open(running_servers_file, "w") as f: # Write each server info to a new line - f.writelines([info.toFileLine() for info in runningServers]) + f.writelines([info.to_file_line() for info in servers]) except IOError as e: log.info("Unable to write running servers file.") log.exception(e, exc_info=True) -def add_server_entry(pathHash: str, pid: int, port: int, binding: str) -> None: +def add_server_entry(path_hash: str, pid: int, port: int, binding: str) -> None: """Add a new server entry to ~/.ptx/running_servers. This function does not attempt to ensure that an active server doesn't already exist. """ - PURGE_LIMIT = 10 # If more servers active, try to clean up - runningServers = get_running_servers() - newEntry = RunningServerInfo(pathHash=pathHash, pid=pid, port=port, binding=binding) - runningServers.append(newEntry) - if len(runningServers) >= PURGE_LIMIT: + running_servers = get_running_servers() + new_entry = RunningServerInfo( + path_hash=path_hash, pid=pid, port=port, binding=binding + ) + running_servers.append(new_entry) + if len(running_servers) >= PURGE_LIMIT: log.info(f"There are {PURGE_LIMIT} or more servers on file. Cleaning up ...") - runningServers = list(stop_inactive_servers(runningServers)) - save_running_servers(runningServers) - log.info(f"Added server entry {newEntry.toFileLine()}") + running_servers = list(stop_inactive_servers(running_servers)) + save_running_servers(running_servers) + log.info(f"Added server entry {new_entry.to_file_line()}") -def remove_server_entry(pathHash: str) -> None: - remainingServers = [ - info for info in get_running_servers() if info.pathHash != pathHash +def remove_server_entry(path_hash: str) -> None: + remaining_servers = [ + info for info in get_running_servers() if info.path_hash != path_hash ] - save_running_servers(remainingServers) + save_running_servers(remaining_servers) def stop_inactive_servers( @@ -129,15 +135,15 @@ def stop_inactive_servers( ) -> t.Iterator[RunningServerInfo]: """Stops any inactive servers and yields the active ones.""" for server in servers: - if server.isActiveServer(): + if server.is_active_server(): yield server else: server.terminate() -def active_server_for_path_hash(pathHash: str) -> t.Optional[RunningServerInfo]: +def active_server_for_path_hash(path_hash: str) -> t.Optional[RunningServerInfo]: return next( - (info for info in get_running_servers() if info.pathHash == pathHash), + (info for info in get_running_servers() if info.path_hash == path_hash), None, ) @@ -157,7 +163,7 @@ def start_server( callback: t.Callable[[int], None] | None = None, ) -> None: log.info("setting up ...") - pathHash = hash_path(base_dir) + path_hash = hash_path(base_dir) pid = os.getpid() binding = binding_for_access(access) log.info("values set...") @@ -165,6 +171,7 @@ def start_server( # Previously we defined a custom handler to prevent caching, but we don't need to do that anymore. It was causing issues with the _static js/css files inside codespaces for an unknown reason. Might bring this back in the future. # 2024-04-05: try using this again to let Firefox work class RequestHandler(SimpleHTTPRequestHandler): + def __init__(self, *args: t.Any, **kwargs: t.Any): super().__init__(*args, directory=base_dir.as_posix(), **kwargs) @@ -186,7 +193,7 @@ class TCPServer(socketserver.TCPServer): try: with TCPServer((binding, port), RequestHandler) as httpd: log.info("adding server entry") - add_server_entry(pathHash, pid, port, binding) + add_server_entry(path_hash, pid, port, binding) log.info("Starting the server") if callback is not None: callback(port) @@ -197,5 +204,5 @@ class TCPServer(socketserver.TCPServer): log.warning(f"Trying port {port} instead.\n") except KeyboardInterrupt: log.info("Stopping server.") - remove_server_entry(pathHash) + remove_server_entry(path_hash) return diff --git a/pretext/utils.py b/pretext/utils.py index a0dc76e7..93e5fd2a 100644 --- a/pretext/utils.py +++ b/pretext/utils.py @@ -150,8 +150,8 @@ def home_path() -> Path: return Path.home() / ".ptx" -def hash_path(projectPath: Path) -> str: - return sha256(str(projectPath).encode("utf-8")).hexdigest()[:10] +def hash_path(project_path: Path) -> str: + return sha256(str(project_path).encode("utf-8")).hexdigest()[:10] # TODO: is this ever called? @@ -766,7 +766,7 @@ def active_server_port() -> t.Optional[int]: for proc in psutil.process_iter(): if proc is None: continue - if isPretextProc(proc): # type: ignore + if is_pretext_proc(proc): # type: ignore log.debug(f"Found pretext server running with pid {proc.pid}") # Sometimes the process stops but doesn't get removed from the process list. We check if the process is still running by checking its status. if proc.status() not in [psutil.STATUS_RUNNING, psutil.STATUS_SLEEPING]: @@ -804,7 +804,7 @@ def stop_server(port: t.Optional[int] = None) -> None: else: # As before, we look for a pretext process that is a child of a pretext process. This time we terminate that process. for proc in psutil.process_iter(): - if isPretextProc(proc): + if is_pretext_proc(proc): log.debug(f"Terminating process with PID {proc.pid}") proc.terminate() @@ -847,7 +847,7 @@ def latest_version() -> t.Optional[str]: return None -def isPretextProc(proc: psutil.Process) -> bool: +def is_pretext_proc(proc: psutil.Process) -> bool: if proc.name() == "pretext": return False parent = proc.parent() diff --git a/pyproject.toml b/pyproject.toml index 0b0f780a..1c61ac7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ prefig = { extras = ["pycairo"], version = "^0.2.10", optional = true } black = [{version = "^23", python = "3.8"}, {version = "^24.10", python = ">=3.9"}] codechat-server = "^0" flake8 = "^6" +pep8-naming = "^0.14.1" lxml-stubs = "^0" mypy = "^1" pytest = "^7"