Repository: benoitc/gunicorn

Issue:
[ver.20.0.0] The gunicorn config files cannot using '__file__' constants.
I configured app_root that base on the relative path in gunicorn files.

`app_root = os.path.abspath(os.path.join(__file__, '..', '..'))`

now I have occurred when upgrade gunicorn version to 20.0.0 and starting with config file.

`$ venv/bin/gunicorn -c /path/to/app/conf/gunicorn.conf.py wsgi:app`

> Failed to read config file: /path/to/app/conf/gunicorn.conf.py
Traceback (most recent call last):
  File "/path/to/app/venv/lib/python3.7/site-packages/gunicorn/app/base.py", line 102, in get_config_from_filename
    loader.exec_module(mod)
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/path/to/app/conf/gunicorn.conf.py", line 9, in <module>
    app_root = os.path.abspath(os.path.join(__file__, '..', '..'))

Thanks to help me.


Discussion:
I encountered the same issue with use of `__file__` in a config file after upgrading to v20. 

Looks like in v19 `__file__` was provided in the scope in which the config file's code was executed:
https://github.com/benoitc/gunicorn/blob/2bf26c1c86472b50508155a8ffde6c80e91099bc/gunicorn/app/base.py#L93-L113

Now `__file__` is never set during the module load process: https://github.com/benoitc/gunicorn/blob/e764accc368506560082f7f684a4172b4f68a8af/gunicorn/app/base.py#L93-L109

Loading the module as suggested in the Python docs winds up setting `__file__` as expected but only works in Python 3.5+ (https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly)
```python
import importlib.util
spec = importlib.util.spec_from_file_location(module_name, filename)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
```

Not sure what the best approach is in Python 3.4. Just setting the `__file__` attribute on the module?
```python
mod = types.ModuleType(module_name)
mod.__file__ = filename
...
```
@berkerpeksag what would you suggest there?
do we want to have support for Python < 3.5 ? I think we could remove that support eventually even for a minor release as it's not supported anymore. Let me know @berkerpeksag @tilgovi @jamadden 

Resolving patch:
diff --git a/gunicorn/__init__.py b/gunicorn/__init__.py
--- a/gunicorn/__init__.py
+++ b/gunicorn/__init__.py
@@ -3,6 +3,6 @@
 # This file is part of gunicorn released under the MIT license.
 # See the NOTICE for more information.
 
-version_info = (20, 0, 0)
+version_info = (20, 0, 1)
 __version__ = ".".join([str(v) for v in version_info])
 SERVER_SOFTWARE = "gunicorn/%s" % __version__
diff --git a/gunicorn/app/base.py b/gunicorn/app/base.py
--- a/gunicorn/app/base.py
+++ b/gunicorn/app/base.py
@@ -2,11 +2,10 @@
 #
 # This file is part of gunicorn released under the MIT license.
 # See the NOTICE for more information.
-import importlib.machinery
+import importlib.util
 import os
 import sys
 import traceback
-import types
 
 from gunicorn import util
 from gunicorn.arbiter import Arbiter
@@ -97,9 +96,10 @@ def get_config_from_filename(self, filename):
 
         try:
             module_name = '__config__'
-            mod = types.ModuleType(module_name)
-            loader = importlib.machinery.SourceFileLoader(module_name, filename)
-            loader.exec_module(mod)
+            spec = importlib.util.spec_from_file_location(module_name, filename)
+            mod = importlib.util.module_from_spec(spec)
+            sys.modules[module_name] = mod
+            spec.loader.exec_module(mod)
         except Exception:
             print("Failed to read config file: %s" % filename, file=sys.stderr)
             traceback.print_exc()
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,6 @@
     'Operating System :: POSIX',
     'Programming Language :: Python',
     'Programming Language :: Python :: 3',
-    'Programming Language :: Python :: 3.4',
     'Programming Language :: Python :: 3.5',
     'Programming Language :: Python :: 3.6',
     'Programming Language :: Python :: 3.7',


Test patch: