Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
14 changes: 14 additions & 0 deletions py/visdom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1733,6 +1733,11 @@ def scatter(self, X, Y=None, win=None, env=None, opts=None, update=None, name=No

@pytorch_wrap
def line(self, Y, X=None, win=None, env=None, opts=None, update=None, name=None):
import numpy as np
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement is incorrectly placed at the function definition level instead of inside the function body. It should be indented to be inside the function (after the docstring at line 1769), not at the same indentation level as the def statement.

Copilot uses AI. Check for mistakes.

Y = np.array(Y)
if np.any(np.isnan(Y)):
Y = np.ma.masked_invalid(Y)
Comment on lines +1738 to +1740
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation of these lines is incorrect. They should be indented at the same level as the function definition (line 1735), not inside the function body. This makes the Y array conversion and NaN checking execute before the function's docstring, which is syntactically incorrect Python. These lines should be moved after the docstring (after line 1768).

Suggested change
Y = np.array(Y)
if np.any(np.isnan(Y)):
Y = np.ma.masked_invalid(Y)
Y = np.array(Y)
if np.any(np.isnan(Y)):
Y = np.ma.masked_invalid(Y)

Copilot uses AI. Check for mistakes.
"""
This function draws a line plot. It takes in an `N` or `NxM` tensor
`Y` that specifies the values of the `M` lines (that connect `N` points)
Expand Down Expand Up @@ -1761,6 +1766,7 @@ def line(self, Y, X=None, win=None, env=None, opts=None, update=None, name=None)
If `update` is specified, the figure will be updated without
creating a new plot -- this can be used for efficient updating.
"""

if update is not None:
if update == "remove":
return self.scatter(
Expand Down Expand Up @@ -1805,6 +1811,14 @@ def line(self, Y, X=None, win=None, env=None, opts=None, update=None, name=None)
labels = np.arange(1, Y.shape[1] + 1)
labels = np.tile(labels, (Y.shape[0], 1)).ravel(order="F")

win = 'line_' + env + str(idx)
return _send({
'data': Y.tolist(), # Send cleaned data
'win': win,
'eid': env,
'layout': layout,
'opts': opts})
Comment on lines +1815 to +1820
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Early return from the function bypasses the original scatter logic.

If the early return is intentional, remove the unreachable scatter logic below. Otherwise, clarify when each path should be used.

Comment on lines 1736 to +1820
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions adding error handling for WebSocket crashes, but the line() function changes in this file (NaN masking, win generation, custom _send call) are unrelated to WebSocket error handling. These changes appear to modify the line plotting API rather than fixing Firefox WebSocket issues. This indicates either scope creep or changes that don't align with the stated purpose of the PR.

Copilot uses AI. Check for mistakes.

Comment on lines +1814 to +1821
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block references undefined variables and breaks the existing function flow. The variables 'idx', 'layout', and '_send' are not defined in this context. Additionally, this early return bypasses all the existing line plot logic (lines 1770-1823) that handles X/Y shape validation, data transformation, and the proper scatter plot call. This completely breaks the line() function's intended behavior.

Suggested change
win = 'line_' + env + str(idx)
return _send({
'data': Y.tolist(), # Send cleaned data
'win': win,
'eid': env,
'layout': layout,
'opts': opts})

Copilot uses AI. Check for mistakes.
return self.scatter(
X=linedata, Y=labels, opts=opts, win=win, env=env, update=update, name=name
)
Comment on lines 1822 to 1824
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code creates unreachable code. Lines 1815-1820 return early from the function, making the subsequent return statement at lines 1822-1824 (which contains the actual intended line plot logic) unreachable and non-functional.

Copilot uses AI. Check for mistakes.
Expand Down
22 changes: 22 additions & 0 deletions py/visdom/server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,25 @@ def load_user_settings(self):
settings["user_css"] = user_css

return settings
class SocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True # Or customize for security
Comment on lines +221 to +222
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Allowing all origins in check_origin may introduce security risks.

Disabling origin checks exposes the WebSocket to CSRF and similar attacks. Restrict allowed origins or make this configurable for production.

Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check_origin implementation returns True unconditionally, which disables Cross-Origin Resource Sharing (CORS) protection. This creates a security vulnerability by allowing WebSocket connections from any origin. The existing SocketHandler in socket_handlers.py inherits from BaseWebSocketHandler which doesn't override check_origin, relying on Tornado's default secure behavior. If CORS protection needs to be relaxed, it should be done with proper validation rather than accepting all origins.

Suggested change
return True # Or customize for security
# Delegate to Tornado's default origin checking for security.
return super().check_origin(origin)

Copilot uses AI. Check for mistakes.

def open(self):
try:
logging.info("WebSocket connection opened")
# Existing open logic (e.g., register client)
except Exception as e:
logging.error(f"WebSocket open error: {e}")

def on_message(self, message):
try:
# Existing message handling logic
pass
except Exception as e:
logging.error(f"WebSocket message error: {e}")
self.close() # Gracefully close on error
Comment on lines +231 to +237
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Closing the WebSocket on any message error may be overly aggressive.

Consider handling exceptions more selectively, logging non-critical errors, and only closing the connection for serious issues.

Suggested change
def on_message(self, message):
try:
# Existing message handling logic
pass
except Exception as e:
logging.error(f"WebSocket message error: {e}")
self.close() # Gracefully close on error
class CriticalWebSocketError(Exception):
"""Exception for critical WebSocket errors that require closing the connection."""
pass
def on_message(self, message):
try:
# Existing message handling logic
pass
except CriticalWebSocketError as e:
logging.error(f"Critical WebSocket message error: {e}")
self.close() # Gracefully close on critical error
except Exception as e:
logging.warning(f"Non-critical WebSocket message error: {e}")
# Optionally, send an error message to the client or take other action


def on_close(self):
logging.info("WebSocket connection closed")
# Existing cleanup logic
Comment on lines +220 to +241
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This SocketHandler class is being added to the wrong file and duplicates existing functionality. The SocketHandler class already exists in py/visdom/server/handlers/socket_handlers.py (line 298). Adding error handling should be done by modifying the existing SocketHandler class in socket_handlers.py, not by creating a new duplicate class in app.py. This implementation also lacks the necessary initialization and integration with the existing Visdom architecture (missing initialize method, subs/sources registration, etc.).

Suggested change
class SocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True # Or customize for security
def open(self):
try:
logging.info("WebSocket connection opened")
# Existing open logic (e.g., register client)
except Exception as e:
logging.error(f"WebSocket open error: {e}")
def on_message(self, message):
try:
# Existing message handling logic
pass
except Exception as e:
logging.error(f"WebSocket message error: {e}")
self.close() # Gracefully close on error
def on_close(self):
logging.info("WebSocket connection closed")
# Existing cleanup logic

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +241
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description states that changes were made to socket_handlers.py, but the actual changes are in app.py instead. The SocketHandler class should be modified in py/visdom/server/handlers/socket_handlers.py where it's actually defined (line 298), not added as a duplicate in app.py. This discrepancy suggests the wrong file was modified.

Copilot uses AI. Check for mistakes.
Loading