Repository: gradio-app/gradio
Pull request: Fixes port issue; deprecates server_port, server_name parameter
Fixes #419. Deprecates `server_port` and `server_name` in `Interface()` class because it was getting messy to support it. These are now parameters of the `launch()` method

Problem statement:
[Suggestions] Don't autofind a port when a port is user specified
**Is your feature request related to a problem? Please describe.**

When using `iface = gr.Interface(.., server_port=5000)` if port `5000` is already used it will use port `5001` instead.

**Describe the solution you'd like**

If it could crash instead that would be better saying the port is already used.

**Additional context**
This was an issue when reloading regularly the gradio app and reloading pages on the browser

Hints from issue discussion:
Makes sense, we'll fix it!

Commit message: allows you to fix port; deprecates server_port, server_name parameters in Interface()

Patch to review:
diff --git a/gradio/interface.py b/gradio/interface.py
index 557187d2ce..7884f0a76c 100644
--- a/gradio/interface.py
+++ b/gradio/interface.py
@@ -63,9 +63,8 @@ def __init__(self, fn, inputs=None, outputs=None, verbose=None, examples=None,
                  examples_per_page=10, live=False, layout="unaligned", show_input=True, show_output=True,
                  capture_session=None, interpretation=None, num_shap=2.0, theme=None, repeat_outputs_per_model=True,
                  title=None, description=None, article=None, thumbnail=None,
-                 css=None, server_port=None, server_name=None, height=500, width=900,
-                 allow_screenshot=True, allow_flagging=None, flagging_options=None, encrypt=False,
-                 show_tips=None, flagging_dir="flagged", analytics_enabled=None, enable_queue=None, api_mode=None):
+                 css=None, height=500, width=900, allow_screenshot=True, allow_flagging=None, flagging_options=None, 
+                 encrypt=False, show_tips=None, flagging_dir="flagged", analytics_enabled=None, enable_queue=None, api_mode=None):
         """
         Parameters:
         fn (Callable): the function to wrap an interface around.
@@ -162,12 +161,7 @@ def __init__(self, fn, inputs=None, outputs=None, verbose=None, examples=None,
                 "Examples argument must either be a directory or a nested list, where each sublist represents a set of inputs.")
         self.num_shap = num_shap
         self.examples_per_page = examples_per_page
-        
-        self.server_name = server_name
-        self.server_port = server_port
-        if server_name is not None or server_port is not None:
-            warnings.warn("The server_name and server_port parameters in the `Interface` class will be deprecated. Please provide them in the `launch()` method instead.")
-        
+                
         self.simple_server = None
         self.allow_screenshot = allow_screenshot
         # For allow_flagging and analytics_enabled: (1) first check for parameter, (2) check for environment variable, (3) default to True
@@ -573,7 +567,6 @@ def launch(self, inline=None, inbrowser=None, share=False, debug=False,
         path_to_local_server (str): Locally accessible link
         share_url (str): Publicly accessible link (if share=True)
         """
-
         # Set up local flask server
         config = self.get_config_file()
         self.config = config
@@ -589,8 +582,8 @@ def launch(self, inline=None, inbrowser=None, share=False, debug=False,
                 getpass.getpass("Enter key for encryption: "))
 
         # Store parameters
-        server_name = server_name or self.server_name or networking.LOCALHOST_NAME
-        server_port = server_port or self.server_port or networking.INITIAL_PORT_VALUE
+        server_name = server_name or networking.LOCALHOST_NAME
+        server_port = server_port
         if self.enable_queue is None:
             self.enable_queue = enable_queue
 
@@ -696,17 +689,19 @@ def launch(self, inline=None, inbrowser=None, share=False, debug=False,
 
         return app, path_to_local_server, share_url
 
-    def close(self):
+    def close(self, verbose=True):
         """
         Closes the Interface that was launched. This will close the server and free the port.
         """
         try:
             if self.share_url:
                 requests.get("{}/shutdown".format(self.share_url))
-                print("Closing Gradio server on port {}...".format(self.server_port))
+                if verbose:
+                    print("Closing Gradio server on port {}...".format(self.server_port))
             elif self.local_url:
                 requests.get("{}shutdown".format(self.local_url))
-                print("Closing Gradio server on port {}...".format(self.server_port))
+                if verbose:
+                    print("Closing Gradio server on port {}...".format(self.server_port))
             else:
                 pass # server not running
         except (requests.ConnectionError, ConnectionResetError):
@@ -751,9 +746,9 @@ def integrate(self, comet_ml=None, wandb=None, mlflow=None):
                 utils.integration_analytics(data)
 
 
-def close_all():
+def close_all(verbose=True):
     for io in Interface.get_instances():
-        io.close()
+        io.close(verbose)
 
 
 def reset_all():
diff --git a/gradio/networking.py b/gradio/networking.py
index 7afc95aa68..0d55570381 100644
--- a/gradio/networking.py
+++ b/gradio/networking.py
@@ -32,7 +32,7 @@
 INITIAL_PORT_VALUE = int(os.getenv(
     'GRADIO_SERVER_PORT', "7860"))  # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
 TRY_NUM_PORTS = int(os.getenv(
-    'GRADIO_NUM_PORTS', "100"))  # Number of ports to try before giving up and throwing an exception.
+    'GRADIO_NUM_PORTS', "1000"))  # Number of ports to try before giving up and throwing an exception.
 LOCALHOST_NAME = os.getenv(
     'GRADIO_SERVER_NAME', "127.0.0.1")
 GRADIO_API_SERVER = "https://api.gradio.app/v1/tunnel-request"
@@ -426,10 +426,20 @@ def queue_thread(path_to_local_server, test_mode=False):
             break
 
 
-def start_server(interface, server_name, server_port, auth=None, ssl=None):
-    port = get_first_available_port(
-        server_port, server_port + TRY_NUM_PORTS
-    )
+def start_server(interface, server_name, server_port=None, auth=None, ssl=None):
+    if server_port is None:  # if port is not specified, start at 7860 and search for first available port        
+        port = get_first_available_port(
+            INITIAL_PORT_VALUE, INITIAL_PORT_VALUE + TRY_NUM_PORTS
+        )
+    else:
+        try:
+            s = socket.socket()  # create a socket object
+            s.bind((LOCALHOST_NAME, server_port))  # Bind to the port to see if it's available (otherwise, raise OSError)
+            s.close()
+        except OSError:
+            raise OSError("Port {} is in use. If a gradio.Interface is running on the port, you can close() it or gradio.close_all().".format(server_port))
+        port = server_port
+
     url_host_name = "localhost" if server_name == "0.0.0.0" else server_name
     path_to_local_server = "http://{}:{}/".format(url_host_name, port)
     if auth is not None:
diff --git a/test/test_networking.py b/test/test_networking.py
index 4adc7c3d9f..0990e31de9 100644
--- a/test/test_networking.py
+++ b/test/test_networking.py
@@ -199,7 +199,6 @@ def test_state_initialization(self):
 
     def test_state_value(self):
         io = gr.Interface(lambda x: len(x), "text", "label")
-        io.launch(prevent_thread_lock=True)
         app, _, _ = io.launch(prevent_thread_lock=True)
         with app.test_request_context():
             networking.set_state("test")


Code review comments:
diff_hunk: @@ -696,17 +689,19 @@ def launch(self, inline=None, inbrowser=None, share=False, debug=False,
 
         return app, path_to_local_server, share_url
 
-    def close(self):
+    def close(self, verbose=True):
         """
         Closes the Interface that was launched. This will close the server and free the port.
         """
         try:
             if self.share_url:
                 requests.get("{}/shutdown".format(self.share_url))
-                print("Closing Gradio server on port {}...".format(self.server_port))
+                if verbose:
+                    print("Closing Gradio server on port {}...".format(self.server_port))
             elif self.local_url:
                 requests.get("{}shutdown".format(self.local_url))
path: gradio/interface.py
text: @user1:
is this missing a forward slash?

diff_hunk: @@ -32,7 +32,7 @@
 INITIAL_PORT_VALUE = int(os.getenv(
     'GRADIO_SERVER_PORT', "7860"))  # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
 TRY_NUM_PORTS = int(os.getenv(
-    'GRADIO_NUM_PORTS', "100"))  # Number of ports to try before giving up and throwing an exception.
+    'GRADIO_NUM_PORTS', "1000"))  # Number of ports to try before giving up and throwing an exception.
path: gradio/networking.py
text: @user1:
Is there a reason this is so high? Seems unnecessarily exhaustive. In my opinion the gradio launch should not hang for that long (unless that process is actually faster than I think), and should quit after a reasonable number of ports and request a defined `server_port`

@author:
Sure