Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions pretext/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,30 +612,31 @@ def view(
log.debug(e, exc_info=True)
return

port = port or 8128
# Call generate if flag is set
if generate:
try:
target.generate_assets(only_changed=False)
except Exception as e:
log.info(f"Failed to generate assets: {e}")
log.debug("", exc_info=True)
# Call build if flag is set
if build:
try:
target.build()
except Exception as e:
log.info(f"Failed to build: {e}")
log.debug("Exception info:\n##################\n", exc_info=True)
# Start server if there isn't one running already:
if not utils.server_running(port=port) or restart_server:
used_port = utils.server_is_running()
if port or restart_server or not used_port:
# First terminate any server that might be running
utils.stop_server()
utils.stop_server(port)
# Start the server
log.info("Starting server.")
server = project.server_process(
output_dir=target.output_dir_abspath(),
access=access,
port=port,
port=port or 8128,
launch=not no_launch,
)
server.start()
Expand All @@ -646,15 +647,15 @@ def view(
log.info("Stopping server.")
server.terminate()
return

url = (
"http://localhost:"
+ str(port)
+ target.output_dir_abspath()
.as_posix()
.replace(project.abspath().as_posix(), "")
)
log.info(f"Viewing output for {target.name} at {url}")
else:
url = (
"http://localhost:"
+ str(used_port)
+ target.output_dir_abspath()
.as_posix()
.replace(project.abspath().as_posix(), "")
)
log.info(f"Viewing output for {target.name} at {url}")


# pretext deploy
Expand Down
27 changes: 0 additions & 27 deletions pretext/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,33 +835,6 @@ def generate_assets(
self.save_asset_table(saved_asset_table)
log.debug(f"Saved asset table to disk: {saved_asset_table}")

def view(
self,
access: t.Literal["public", "private"],
port: int = 8000,
watch: bool = False,
no_launch: bool = False,
) -> None:
directory = self.output_dir_abspath()

if watch:
watch_directory = self.source_abspath()
else:
watch_directory = None
if not self.output_dir_abspath().exists():
log.error(f"The directory `{self.output_dir_abspath()}` does not exist.")
log.error(
f"Run `pretext view {self.name} -b` to build your project before viewing."
)
return

def watch_callback() -> None:
self.build()

utils.run_server(
directory, access, port, watch_directory, watch_callback, no_launch
)


class Project(pxml.BaseXmlModel, tag="project", search_mode="unordered"):
"""
Expand Down
81 changes: 31 additions & 50 deletions pretext/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import tempfile
import threading
import psutil
import urllib.request
import watchdog.events
import watchdog.observers
import time
Expand Down Expand Up @@ -297,34 +296,6 @@ class TCPServer(socketserver.TCPServer):
log.warning(f"Trying port {port} instead.\n")


def run_server(
directory: Path,
access: t.Literal["public", "private"],
port: int,
watch_directory: t.Optional[Path] = None,
watch_callback: Callable[[], None] = lambda: None,
no_launch: bool = False,
) -> None:
threading.Thread(
target=lambda: serve_forever(directory, access, port, no_launch), daemon=True
).start()
if watch_directory is not None:
log.info(f"\nWatching for changes in `{watch_directory}` ...\n")
event_handler = HTMLRebuildHandler(watch_callback)
observer = watchdog.observers.Observer()
observer.schedule(event_handler, watch_directory, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
log.info("\nClosing server...")
if watch_directory is not None:
observer.stop()
if watch_directory is not None:
observer.join()


# Info on namespaces: http://lxml.de/tutorial.html#namespaces
NSMAP = {
"xi": "http://www.w3.org/2001/XInclude",
Expand Down Expand Up @@ -714,39 +685,49 @@ def retrieve(temp: Path, folder: Path) -> None:
shutil.rmtree(temp)


def server_running(port: int) -> bool:
def server_is_running() -> t.Optional[int]:
"""
Check if a pretext-view server is running already
Check if a pretext-view server is running already, and if so, return its port number.
"""
# We look at all currently running processes and check if any are a pretext process that is a child of a pretext process. This would only happen if we have run a `pretext view` command to start the server, so we can assume that this is the server we are looking for.
for proc in psutil.process_iter():
if proc.name() == "pretext" and proc.parent().name() == "pretext":
log.debug(f"Found pretext server running with pid {proc.pid}")
if proc.status() == psutil.STATUS_STOPPED:
# 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]:
log.debug("Server is stopped.")
return False
log.debug(f"Checking if port {port} is an html host:")
return None
# To get the port number, we look at the connections the process has open. We assume that the server is running on the first port that is open.
try:
with urllib.request.urlopen(f"http://localhost:{port}") as response:
log.debug(f"Found html host running on port {port}")
# We have found the right server if one of the files shown is project.ptx
if "project.ptx" in response.read().decode("utf-8"):
log.debug("Found project.ptx in html host.")
return True
port = proc.connections()[0].laddr.port
log.debug(f"Server is running on port {port}")
return port
except Exception as e:
log.debug(f"Did not find html: {e}")
log.debug(f"No html host running on port {port}")
return False
log.debug(f"Could not get port number. Exception: {e}", exc_info=True)
log.debug("No pretext server found running.")
return False
return None


def stop_server() -> None:
def stop_server(port: t.Optional[int] = None) -> None:
"""
Terminate a server running in the background.
"""
# 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 proc.name() == "pretext" and proc.parent().name() == "pretext":
log.debug(f"Terminating process with PID {proc.pid}")
proc.terminate()
# If a port was passed, we look for a "pretext" process that has a connection on that port, and kill it if we find it.
if port is not None:
log.debug(f"Terminating server running on port {port}")
for proc in psutil.process_iter():
if len(proc.connections()) > 0:
if (
proc.name() == "pretext"
and proc.connections()[0].laddr.port == port
):
log.debug(
f"Terminating process with PID {proc.pid} hosting on port {port}"
)
proc.terminate()
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 proc.name() == "pretext" and proc.parent().name() == "pretext":
log.debug(f"Terminating process with PID {proc.pid}")
proc.terminate()