Repository: sanic-org/sanic

Issue:
Module import fails when auto_reload is active
I have two piece of code (the structure has been simplified for clarity sake)
First in base.py
```
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# module: init0

from abc   import ABCMeta
from sanic import Sanic

class BaseService( metaclass = ABCMeta ):
    def create_app( self ) -> Sanic:
        app = Sanic( __name__ )
        return app

# app = BaseService().create_app()

# app.run( host  = '0.0.0.0',
#      port  = 5000,
#      debug = True,
#     )

print('[DONE]')

```
Second in run_test.py
```
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# module: init0

from init0.base import BaseService

def main():
    app = BaseService().create_app()
    app.run( host  = '0.0.0.0',
         port  = 5000,
         debug = True,
        )
    return

if __name__ == '__main__':
    main()

```

If I were to run `python -m init0.run_test` with `debug = False` then everything works perfectly, however if it's `debug = True`, then it'd throw me `ModuleNotFoundError: No module named 'init0'`

Is it some sort of loading error somewhere that I need to configure beforehand?

Thanks a lot in advance


Discussion:
What does your `__init__.py` look like for that directory?
> What does your `__init__.py` look like for that directory?

Completely empty. I updated my Sanic recently from 0.8 to 18.x along with my Python 3.6 to 3.7 or something, and somehow this issue appeared.
How does your project hierarchy look like ?
```
.
├── init0
│   ├── __init__.py
│   ├── __pycache__
│   ├── base.py
│   └── run_test.py
└── venv
    ├── bin
    ├── include
    └── lib
```
I just run from `python -m init0.runtest`
I don't think your python interpreter know your init0 module unless you've appended that to your sys.path
@yunstanford Hmm, but it works okay when it's not in debug mode, though.
ok, i think it's sth. wrong with the auto_reload logic..

You can set (`workers > 1` or `debug=false`) for verifying, since the auto_reload will be disabled in such cases.
Yeap, by setting it to
```
app.run( host  = '0.0.0.0',
         port  = 5000,
         debug = True,
         workers = 2,
        )
```
the error no longer occurs.

I hit the same error, adding workers >1 works for me as well!
@huge-success/sanic-core-devs Anyone want to [take a whack at this?](https://community.sanicframework.org/t/march-19-03-release/231)
@subokita @gangtao 
Are you on Linux/Mac, or Windows?
The auto_reload logic is a bit different between Unix/Posix based OS, and Windows, so it would be good to know which we are dealing with before diving into debugging this.
@ashleysommer 
```
OSX Mojave 10.14.4 Beta (18E194d)
Python     3.7.2
sanic      18.12.0
uvloop     0.12.1
gevent     1.4.0
```
To be clear, this issue is not because of windows.

When invoking python interpreter with `-m` option, the current directory will be added to the start of sys.path.

However, The reload_logic use https://github.com/huge-success/sanic/blob/master/sanic/reloader_helpers.py#L36-L57 to start worker process. That command line will look like `python /path/to/start.py`, because if invoking python interpreter with `-m` option, the first element of sys.argv will be the full path to the module file.

The reload implementation is not robust (and buggy), as mentioned before.
I'd also prefer to re-implement the reload logic as @vltr discussed here, https://github.com/huge-success/sanic/issues/1346
And I personally use gunicorn for reloading in dev mode.

Resolving patch:
diff --git a/sanic/reloader_helpers.py b/sanic/reloader_helpers.py
--- a/sanic/reloader_helpers.py
+++ b/sanic/reloader_helpers.py
@@ -36,7 +36,15 @@ def _iter_module_files():
 def _get_args_for_reloading():
     """Returns the executable."""
     rv = [sys.executable]
-    rv.extend(sys.argv)
+    main_module = sys.modules["__main__"]
+    mod_spec = getattr(main_module, "__spec__", None)
+    if mod_spec:
+        # Parent exe was launched as a module rather than a script
+        rv.extend(["-m", mod_spec.name])
+        if len(sys.argv) > 1:
+            rv.extend(sys.argv[1:])
+    else:
+        rv.extend(sys.argv)
     return rv
 
 
@@ -44,6 +52,7 @@ def restart_with_reloader():
     """Create a new process and a subprocess in it with the same arguments as
     this one.
     """
+    cwd = os.getcwd()
     args = _get_args_for_reloading()
     new_environ = os.environ.copy()
     new_environ["SANIC_SERVER_RUNNING"] = "true"
@@ -51,7 +60,7 @@ def restart_with_reloader():
     worker_process = Process(
         target=subprocess.call,
         args=(cmd,),
-        kwargs=dict(shell=True, env=new_environ),
+        kwargs={"cwd": cwd, "shell": True, "env": new_environ},
     )
     worker_process.start()
     return worker_process


Test patch: