internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: encode/uvicorn

Issue:
Uvicorn should handle headers as any iterables, not just lists (ASGI spec compliance)
[ASGI Specification](https://asgi.readthedocs.io/en/latest/specs/www.html#response-start) in the `Response Start` says that if I send `http.response.start`, the `headers` part should be of the following form:

(Iterable[[byte string, byte string]])

>  A iterable of [name, value] two-item iterables, where name is the header name, and value is the header value. Order must be preserved in the HTTP response. Header names must be lowercased. Optional; defaults to an empty list.

So let's have an ASGI compliant application of the following form:

```
async def application(scope, receive, send):
    await send(
        {
            "type": "http.response.start",
            "headers": ([b"content-type", b"text/plain"],),
            "status": 200,
        }
    )
    await send({"type": "http.response.body", "body": b"dupa"})
```

After requesting this I get the following in the console:

```
ERROR: Exception in ASGI application
Traceback (most recent call last):
  File "/home/jerzy/venv/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 368, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "./asgi.py", line 9, in application
    "status": 200,
  File "/home/jerzy/venv/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 427, in send
    headers = self.default_headers + message.get("headers", [])
TypeError: can only concatenate list (not "tuple") to list

```

Looks like the headers have to be list of list, not simple iterable of 2-items iterable. Do you think this is important? If we want to be 100% compliant with ASGI spec uvicorn should handle just any iterables. If you agree, do you think I could prepare a patch?


Discussion:
Seems reasonable, yup.
I guess `self.default_headers + list(message.get("headers", []))` would do the trick.

Resolving patch:
diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py
--- a/uvicorn/protocols/http/httptools_impl.py
+++ b/uvicorn/protocols/http/httptools_impl.py
@@ -424,7 +424,7 @@ async def send(self, message):
             self.waiting_for_100_continue = False
 
             status_code = message["status"]
-            headers = self.default_headers + message.get("headers", [])
+            headers = self.default_headers + list(message.get("headers", []))
 
             if self.access_log:
                 self.logger.info(


Test patch:
Repository: encode/uvicorn

Issue:
Allow invocation with runpy
In some environments, console scripts are not installed or interfere with different python versions. For maximum compatibility, uvicorn could offer a [runpy](https://docs.python.org/3/library/runpy.html) entry point, which would allow it to be invoked in any Python environment where it's installed without any console scripts (such as with [pip-run](https://pypi.org/project/pip-run)).


Discussion:

Resolving patch:
diff --git a/uvicorn/__main__.py b/uvicorn/__main__.py
new file mode 100644
--- /dev/null
+++ b/uvicorn/__main__.py
@@ -0,0 +1,5 @@
+import uvicorn
+
+
+if __name__ == "__main__":
+    uvicorn.main()


Test patch:
Repository: encode/uvicorn

Issue:
Misleading logs during application startup
Hi,
When running an application with Uvicorn, logs are a bit misleading, especially when running with "INFO" log level.

Because `Waiting for application startup.` is logged before lifespan.startup with INFO level, but no information is logged to signal the end of application startup, giving the idea that the application is stuck at startup event. 'lifespan.startup.complete' can be seen using DEBUG level.

No information is logged to signal when the underlying application is ready to handle requests.

For example:
```bash
$ uvicorn server:app --port 44777 --reload --log-level info

INFO: Uvicorn running on http://127.0.0.1:44777 (Press CTRL+C to quit)
INFO: Started reloader process [8402]
INFO: Started server process [8406]
INFO: Waiting for application startup.  # <-- nothing is logged after this point; the user might think the application is not ready, yet
```

Would you accept a PR to signal with info level when application startup completes? I ask because I already have a [PR open](https://github.com/encode/uvicorn/pull/446) and I don't want to look annoying.



Discussion:
Yes please!

Let's get the messaging tweaked to this:

```
INFO: Waiting for application startup...
INFO: ...application startup complete.
```


Resolving patch:
diff --git a/uvicorn/__main__.py b/uvicorn/__main__.py
--- a/uvicorn/__main__.py
+++ b/uvicorn/__main__.py
@@ -1,5 +1,4 @@
 import uvicorn
 
-
 if __name__ == "__main__":
     uvicorn.main()
diff --git a/uvicorn/lifespan/on.py b/uvicorn/lifespan/on.py
--- a/uvicorn/lifespan/on.py
+++ b/uvicorn/lifespan/on.py
@@ -29,6 +29,8 @@ async def startup(self):
         if self.startup_failed or (self.error_occured and self.config.lifespan == "on"):
             self.logger.error("Application startup failed. Exiting.")
             self.should_exit = True
+        else:
+            self.logger.info("Application startup complete.")
 
     async def shutdown(self):
         if self.error_occured:
@@ -36,6 +38,7 @@ async def shutdown(self):
         self.logger.info("Waiting for application shutdown.")
         await self.receive_queue.put({"type": "lifespan.shutdown"})
         await self.shutdown_event.wait()
+        self.logger.info("Application shutdown complete.")
 
     async def main(self):
         try:
diff --git a/uvicorn/workers.py b/uvicorn/workers.py
--- a/uvicorn/workers.py
+++ b/uvicorn/workers.py
@@ -1,6 +1,7 @@
 import asyncio
 
 from gunicorn.workers.base import Worker
+
 from uvicorn.config import Config
 from uvicorn.main import Server
 


Test patch:
Repository: encode/uvicorn

Issue:
Feature Request: unix server support ssl
`config.ssl` is not passed to `create_unix_server`.

https://github.com/encode/uvicorn/blob/e0a77de6baf436d182868fa280cd5e2f0d66501c/uvicorn/main.py#L368

https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_unix_server


Discussion:

Resolving patch:
diff --git a/uvicorn/main.py b/uvicorn/main.py
--- a/uvicorn/main.py
+++ b/uvicorn/main.py
@@ -365,7 +365,9 @@ async def startup(self, sockets=None):
             uds_perms = 0o666
             if os.path.exists(config.uds):
                 uds_perms = os.stat(config.uds).st_mode
-            server = await loop.create_unix_server(create_protocol, path=config.uds)
+            server = await loop.create_unix_server(
+                create_protocol, path=config.uds, ssl=config.ssl
+            )
             os.chmod(config.uds, uds_perms)
             message = "Uvicorn running on unix socket %s (Press CTRL+C to quit)"
             self.logger.info(message % config.uds)


Test patch:
Repository: encode/uvicorn

Issue:
Add py3.8 to the test matrix
Adds py3.8 to the test matrix


Discussion:
last time I tried uvloop 0.14.0dev0 was not released and requires Cython
0.29.13, but uvloop pinned Cython to 0.29.10

On Mon, Oct 21, 2019 at 4:25 PM Tom Christie <notifications@github.com>
wrote:

> *@tomchristie* commented on this pull request.
> ------------------------------
>
> In .travis.yml
> <https://github.com/encode/uvicorn/pull/453#discussion_r337047822>:
>
> > @@ -29,6 +31,11 @@ install:
>        elif [ "$TRAVIS_PYTHON_VERSION" = "pypy3.5" ]; then
>          pip install -U click h11 wsproto==0.13.*;
>          pip install -U autoflake codecov isort pytest pytest-cov requests;
> +      elif [ "$TRAVIS_PYTHON_VERSION" = "3.8" ]; then
> +        pip install -U Cython;
> +        pip install -U git+https://github.com/MagicStack/uvloop.git@1eb6727437430fe0500d7f044dd6e4432abd0901;
> +        pip install -U click h11 httptools websockets==8.* wsproto==0.13.*;
> +        pip install -U autoflake black codecov isort pytest pytest-cov requests;
>
> How come we need to install cython here?
> I guess ideally we'd have test cases that just skipif a required
> dependency isn't installed.
> Then we can just eg. omit uvloop, until they have a non-dev version that
> supports 3.8.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/encode/uvicorn/pull/453?email_source=notifications&email_token=AAINSPSTIVA2TDCDCFARMKTQPW3ULA5CNFSM4JCQAZD2YY3PNVWWK3TUL52HS4DFWFIHK3DMKJSXC5LFON2FEZLWNFSXPKTDN5WW2ZLOORPWSZGOCIT5UVQ#pullrequestreview-304601686>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAINSPVWQRVWJ4LY5UPJ6UTQPW3ULANCNFSM4JCQAZDQ>
> .
>


-- 
benoit barthelet
http://pgp.mit.edu/pks/lookup?op=get&search=0xF150E01A72F6D2EE


Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,7 @@ def get_packages(package):
     "h11==0.8.*",
     "websockets==8.*",
     "httptools==0.0.13 ;" + env_marker,
-    "uvloop==0.* ;" + env_marker,
+    "uvloop==0.14.0rc2 ;" + env_marker,
 ]
 
 
@@ -63,7 +63,7 @@ def get_packages(package):
     install_requires=requirements,
     data_files = [("", ["LICENSE.md"])],
     classifiers=[
-        'Development Status :: 3 - Alpha',
+        'Development Status :: 4 - Beta',
         'Environment :: Web Environment',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: BSD License',
@@ -72,6 +72,7 @@ def get_packages(package):
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Python :: Implementation :: PyPy',
     ],


Test patch:
Repository: encode/uvicorn

Issue:
Failing `socket_info.getpeername()` with uvloop against a UDS.
When running against a unix domain socket, we seem to occassionaly get an OSError when looking up the remote address. This appears to only occur with `uvloop`.

```
ERROR:    Exception in callback HttpToolsProtocol.connection_made(<_SelectorSoc...e, bufsize=0>>)
handle: <Handle HttpToolsProtocol.connection_made(<_SelectorSoc...e, bufsize=0>>)>
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/events.py", line 127, in _run
    self._callback(*self._args)
  File "/Users/tomchristie/GitHub/encode/uvicorn/uvicorn/protocols/http/httptools_impl.py", line 129, in connection_made
    self.client = get_remote_addr(transport)
  File "/Users/tomchristie/GitHub/encode/uvicorn/uvicorn/protocols/utils.py", line 8, in get_remote_addr
    info = socket_info.getpeername()
OSError: [Errno 22] Invalid argument
```


Discussion:

Resolving patch:
diff --git a/uvicorn/protocols/utils.py b/uvicorn/protocols/utils.py
--- a/uvicorn/protocols/utils.py
+++ b/uvicorn/protocols/utils.py
@@ -4,8 +4,16 @@
 def get_remote_addr(transport):
     socket_info = transport.get_extra_info("socket")
     if socket_info is not None:
-        info = socket_info.getpeername()
-        family = socket_info.family
+        try:
+            info = socket_info.getpeername()
+        except OSError:
+            # This case appears to inconsistently occur with uvloop
+            # bound to a unix domain socket.
+            family = None
+            info = None
+        else:
+            family = socket_info.family
+
         if family in (socket.AF_INET, socket.AF_INET6):
             return (str(info[0]), int(info[1]))
         return None


Test patch:
Repository: encode/uvicorn

Issue:
FORWARDED_ALLOW_IPS cannot be loaded from ENV
The following two lines together makes impossible to read `FORWARDED_ALLOW_IPS` from env variable.

https://github.com/encode/uvicorn/blob/4da64be4335ba50e43d7ac8c3b5535460a9c1594/uvicorn/config.py#L160
https://github.com/encode/uvicorn/blob/4da64be4335ba50e43d7ac8c3b5535460a9c1594/uvicorn/config.py#L193


Discussion:

Resolving patch:
diff --git a/uvicorn/config.py b/uvicorn/config.py
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -157,7 +157,6 @@ def __init__(
         self.reload = reload
         self.workers = workers or 1
         self.proxy_headers = proxy_headers
-        self.forwarded_allow_ips = forwarded_allow_ips or "127.0.0.1"
         self.root_path = root_path
         self.limit_concurrency = limit_concurrency
         self.limit_max_requests = limit_max_requests
@@ -190,8 +189,12 @@ def __init__(
         if workers is None and "WEB_CONCURRENCY" in os.environ:
             self.workers = int(os.environ["WEB_CONCURRENCY"])
 
-        if forwarded_allow_ips is None and "FORWARDED_ALLOW_IPS" in os.environ:
-            self.forwarded_allow_ips = os.environ["FORWARDED_ALLOW_IPS"]
+        if forwarded_allow_ips is None:
+            self.forwarded_allow_ips = os.environ.get(
+                "FORWARDED_ALLOW_IPS", "127.0.0.1"
+            )
+        else:
+            self.forwarded_allow_ips = forwarded_allow_ips
 
     @property
     def is_ssl(self) -> bool:


Test patch:
Repository: encode/uvicorn

Issue:
[BUG] Using --log-config disables uvicorn loggers
### Summary

Uvicorn logs are disabled when starting a uvicorn server process with a custom `--log-config` file.

### Steps to Reproduce

**`logging_config.ini`**
```
[loggers]
keys=root

[handlers]
keys=h

[formatters]
keys=f

[logger_root]
level=INFO
handlers=h

[handler_h]
class=StreamHandler
level=INFO
formatter=f
args=(sys.stderr,)

[formatter_f]
format=%(asctime)s %(name)s %(levelname)-4s %(message)s
```

**`scratch_fastapi.py`**
```
import logging
from fastapi import FastAPI

logger = logging.getLogger(__name__)
app = FastAPI()

@app.on_event("startup")
async def startup():
    logger.warning('Raising an exception on startup')
    raise Exception("Nope!")
```

#### Run without `--log-config`

```
uvicorn scratch_fastapi:app
```
```
INFO:     Started server process [7448]
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Waiting for application startup.
WARNING:  Raising an exception on startup
ERROR:    Traceback (most recent call last):
  File "/Users/peter/.virtualenvs/JR/lib/python3.7/site-packages/starlette/routing.py", line 473, in __call__
    await self.startup()
  File "/Users/peter/.virtualenvs/JR/lib/python3.7/site-packages/starlette/routing.py", line 457, in startup
    await handler()
  File "./scratch_fastapi.py", line 10, in startup
    raise Exception("Nope!")
Exception: Nope!

ERROR:    Application startup failed. Exiting.
```

#### Run with `--log-config`
```
uvicorn scratch_fastapi:app --log-config=logging_config.ini
```
```
2019-12-04 11:56:43,681 scratch_fastapi WARNING Raising an exception on startup
```

Notice that all the logs from uvicorn are gone.

### Expected Behavior

The existing uvicorn logger is not disabled.

### System Information

OS: MacOS Mojave
Python: 3.7.5
Uvicorn version: uvicorn==0.10.8

Pip freeze output for full dependencies list:
```
Click==7.0
dnspython==1.16.0
email-validator==1.0.5
fastapi==0.44.0
h11==0.8.1
httptools==0.0.13
idna==2.8
pydantic==1.2
starlette==0.12.9
uvicorn==0.10.8
uvloop==0.14.0
websockets==8.1
```

### Fix

I have a simple fix [here](https://github.com/petermorrow/uvicorn/tree/bug/fixes-log-config-disables-logger) that I'll follow up with a PR for.


Discussion:

Resolving patch:
diff --git a/uvicorn/config.py b/uvicorn/config.py
--- a/uvicorn/config.py
+++ b/uvicorn/config.py
@@ -218,7 +218,9 @@ def __reduce__(self):
             if isinstance(self.log_config, dict):
                 logging.config.dictConfig(self.log_config)
             else:
-                logging.config.fileConfig(self.log_config)
+                logging.config.fileConfig(
+                    self.log_config, disable_existing_loggers=False
+                )
 
         if self.log_level is not None:
             if isinstance(self.log_level, str):


Test patch:
Repository: encode/uvicorn

Issue:
--reload not working on Windows but --loop asyncio is
Duplicate of https://github.com/encode/uvicorn/issues/477 but per request a new topic to track the issue. Especially useful since `--loop asyncio` does seem to work just fine.

@tomchristie suggests to default to asyncio on all platforms. I can make a PR for that. Do you still want to keep the optional uvloop dependency? (It's incompatible with Windows in any case, so perhaps it should be removed from the requirements file at least.)


Discussion:

Resolving patch:
diff --git a/uvicorn/loops/auto.py b/uvicorn/loops/auto.py
--- a/uvicorn/loops/auto.py
+++ b/uvicorn/loops/auto.py
@@ -5,10 +5,7 @@ def auto_loop_setup():
     try:
         import uvloop
     except ImportError as exc:  # pragma: no cover
-        if sys.platform == "win32":
-            from uvicorn.loops.iocp import iocp_setup as loop_setup
-        else:
-            from uvicorn.loops.asyncio import asyncio_setup as loop_setup
+        from uvicorn.loops.asyncio import asyncio_setup as loop_setup
 
         loop_setup()
     else:


Test patch:
Repository: encode/uvicorn

Issue:
Correctly output selected port if port=0
If you run Uvicorn with `port=0`, it selects an unused port (as [it should](https://www.lifewire.com/port-0-in-tcp-and-udp-818145)). But... it logs the incorrect URL:

```
$ datasette --memory -p 0
Serve! files=() (immutables=()) on port 0
INFO:     Started server process [28673]
INFO:     Uvicorn running on http://127.0.0.1:0 (Press CTRL+C to quit)
```

It should output the selected port in that message instead.


Discussion:

Resolving patch:
diff --git a/uvicorn/main.py b/uvicorn/main.py
--- a/uvicorn/main.py
+++ b/uvicorn/main.py
@@ -453,6 +453,9 @@ async def startup(self, sockets=None):
             except OSError as exc:
                 logger.error(exc)
                 sys.exit(1)
+            port = config.port
+            if port == 0:
+                port = server.sockets[0].getsockname()[1]
             protocol_name = "https" if config.ssl else "http"
             message = "Uvicorn running on %s://%s:%d (Press CTRL+C to quit)"
             color_message = (
@@ -464,7 +467,7 @@ async def startup(self, sockets=None):
                 message,
                 protocol_name,
                 config.host,
-                config.port,
+                port,
                 extra={"color_message": color_message},
             )
             self.servers = [server]


Test patch:
Repository: encode/uvicorn

Issue:
Update httptools compatibility
https://github.com/encode/uvicorn/issues/567


Discussion:
In https://github.com/MagicStack/httptools/issues/52 uvicorn is asked to change it's code so it doesn't rely on httptools' private packages.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -44,7 +44,7 @@ def get_packages(package):
     "click==7.*",
     "h11>=0.8,<0.10",
     "websockets==8.*",
-    "httptools==0.0.13 ;" + env_marker,
+    "httptools==0.1.* ;" + env_marker,
     "uvloop>=0.14.0 ;" + env_marker,
 ]
 
diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py
--- a/uvicorn/protocols/http/httptools_impl.py
+++ b/uvicorn/protocols/http/httptools_impl.py
@@ -156,7 +156,7 @@ def data_received(self, data):
 
         try:
             self.parser.feed_data(data)
-        except httptools.parser.errors.HttpParserError as exc:
+        except httptools.HttpParserError as exc:
             msg = "Invalid HTTP request received."
             self.logger.warning(msg)
             self.transport.close()


Test patch:
Repository: encode/uvicorn

Issue:
Uvicorn via gunicorn worker doesn't respect `--forwarded-allow-ips`
I use uvicorn in docker as uvicorn-worker for gunicorn for my fastapi app. My application needs to know the real client IP of each request, so I use proxy-server with the `X-Forwarded-For` header.

Gunicorn has a special option to change proxy-ip to real-ip, so I running gunicorn like this:
```
gunicorn \
  ppm_telegram_bot.api:app \
  --forwarded-allow-ips="*" 
  --worker-class=uvicorn.workers.UvicornWorker \
  --bind=0.0.0.0:$PORT
```

Because I'm in a container, my WSGI/ASGI server receives requests not from the localhost, but from the docker network.

But uvicorn-worker doesn't respect gunicorn's `forwarded-allow-ips`, so in `ProxyHeadersMiddleware.trusted_hosts` I receive default `127.0.0.1` and proxy-ip instead of real-ip.
https://github.com/encode/uvicorn/blob/9d9f8820a8155e36dcb5e4d4023f470e51aa4e03/uvicorn/middleware/proxy_headers.py#L14-L17

It looks like uvicorn-worker can forward this information to config via `config_kwargs`: https://github.com/encode/uvicorn/blob/9d9f8820a8155e36dcb5e4d4023f470e51aa4e03/uvicorn/workers.py#L28-L35

I could do PR with this change, if required 🙌 


Discussion:
Current workaround — set  `FORWARDED_ALLOW_IPS="*"` as env var before running gunicorn

Resolving patch:
diff --git a/uvicorn/workers.py b/uvicorn/workers.py
--- a/uvicorn/workers.py
+++ b/uvicorn/workers.py
@@ -2,6 +2,7 @@
 import logging
 
 from gunicorn.workers.base import Worker
+
 from uvicorn.config import Config
 from uvicorn.main import Server
 
@@ -32,6 +33,7 @@ def __init__(self, *args, **kwargs):
             "timeout_notify": self.timeout,
             "callback_notify": self.callback_notify,
             "limit_max_requests": self.max_requests,
+            "forwarded_allow_ips": self.cfg.forwarded_allow_ips,
         }
 
         if self.cfg.is_ssl:


Test patch:
Repository: encode/uvicorn

Issue:
Typo in protocol logging.
Line 137 of /protocols/http/h11_impl.py has a typo:
`self.logger.log(TRACE_LOG_LEVEL, "%Connection lost", prefix)` should be `self.logger.log(TRACE_LOG_LEVEL, "%sConnection lost", prefix)`. This causes an exception to be thrown when logging at trace level.


Discussion:

Resolving patch:
diff --git a/uvicorn/protocols/http/h11_impl.py b/uvicorn/protocols/http/h11_impl.py
--- a/uvicorn/protocols/http/h11_impl.py
+++ b/uvicorn/protocols/http/h11_impl.py
@@ -134,7 +134,7 @@ def connection_lost(self, exc):
 
         if self.logger.level <= TRACE_LOG_LEVEL:
             prefix = "%s:%d - " % tuple(self.client) if self.client else ""
-            self.logger.log(TRACE_LOG_LEVEL, "%Connection lost", prefix)
+            self.logger.log(TRACE_LOG_LEVEL, "%sConnection lost", prefix)
 
         if self.cycle and not self.cycle.response_complete:
             self.cycle.disconnected = True


Test patch:
Repository: encode/uvicorn

Issue:
[QUESTION]How to specify application path when running uvicorn outside of the application directory.
I thought there would be an option like `--chdir` in Gunicorn but I didn't find one on the documentation, at first I thought that's what `--root-path` option is used for, turns out I was wrong.


Discussion:
Would also like to know if this is a thing.  This would make deployments much easier.
Using PYTHONPATH maybe, if I understood correctly what you want? 
No, something more like `--chdir backend\src` for deploying uvicorn
I am also looking for something like this. Could you help ?
I think we could probably accept a pull request adding that, yup.

Resolving patch:
diff --git a/uvicorn/main.py b/uvicorn/main.py
--- a/uvicorn/main.py
+++ b/uvicorn/main.py
@@ -256,6 +256,13 @@ def print_version(ctx, param, value):
     is_eager=True,
     help="Display the uvicorn version and exit.",
 )
+@click.option(
+    "--app-dir",
+    "app_dir",
+    default=".",
+    show_default=True,
+    help="Look for APP in the specified directory, by adding this to the PYTHONPATH. Defaults to the current working directory.",
+)
 def main(
     app,
     host: str,
@@ -290,8 +297,9 @@ def main(
     ssl_ciphers: str,
     headers: typing.List[str],
     use_colors: bool,
+    app_dir: str,
 ):
-    sys.path.insert(0, ".")
+    sys.path.insert(0, app_dir)
 
     kwargs = {
         "app": app,


Test patch: