Repository: dynaconf/dynaconf

Issue:
[bug]AttributeError when use `load_file` to load `pathlib.Path`
**Describe the bug**

```
Traceback (most recent call last):
  File "/home/kevin/.virtualenvs/pythonic-project-samples-Yf6zzgRF/lib/python3.7/site-packages/dynaconf/loaders/py_loader.py", line 61, in try_to_load_from_py_module_name
    mod = importlib.import_module(name)
  File "/usr/lib/python3.7/importlib/__init__.py", line 118, in import_module
    if name.startswith('.'):
AttributeError: 'PosixPath' object has no attribute 'startswith'

```

**To Reproduce**
Steps to reproduce the behavior:

1. Having the following folder structure

<!-- Describe or use the command `$ tree -v` and paste below -->

<details>
<summary> Project structure </summary>

```bash
.
├── LICENSE
├── Pipfile
├── Pipfile.lock
├── README.md
├── build
│   ├── bdist.linux-x86_64
│   └── lib
│       └── example_blog
│           └── __init__.py
├── dist
│   └── example_blog-0.1.0-py3-none-any.whl
├── docs
│   └── history.md
├── migration
│   ├── README
│   ├── alembic.ini
│   ├── env.py
│   ├── script.py.mako
│   └── versions
│       ├── 4dc5260062e0_init.py
│       └── 47bad9d4be5e_init_table.py
├── setup.cfg
├── setup.py
├── src
│   ├── example_blog
│   │   ├── __init__.py
│   │   ├── cmdline.py
│   │   ├── config
│   │   │   ├── __init__.py
│   │   │   ├── settings.local.yml
│   │   │   └── settings.yml
│   │   ├── dao.py
│   │   ├── db.py
│   │   ├── dependencies.py
│   │   ├── exceptions.py
│   │   ├── log.py
│   │   ├── middlewares.py
│   │   ├── models.py
│   │   ├── routes.py
│   │   ├── schemas.py
│   │   ├── server.py
│   │   ├── services.py
│   │   ├── utils.py
│   │   └── views
│   │       ├── __init__.py
│   │       ├── article.py
│   │       ├── auth.py
│   │       └── user.py
│   └── example_blog.egg-info
│       ├── PKG-INFO
│       ├── SOURCES.txt
│       ├── dependency_links.txt
│       ├── entry_points.txt
│       ├── requires.txt
│       └── top_level.txt
└── tests
    ├── __init__.py
    ├── conftest.py
    ├── settings.yml
    ├── test_app.py
    ├── test_dao.py
    ├── test_service.py
    ├── test_utils.py
    └── tests.py
```
</details>

2. Having the following config files:

<!-- Please adjust if you are using different files and formats! -->

<details>
<summary> Config files </summary>

**pythonic-project-samples/src/example_blog/config/settings.yml**
```yaml
DEBUG: False
LOG_PATH:

HOST: 127.0.0.1
PORT: 8000

# # https://docs.sqlalchemy.org/en/13/core/engines.html
#DATABASE:
#  DRIVER: sqlite
#  NAME: example_blog.sqlite

DATABASE:
  DRIVER: mysql
  DATABASE: example_blog
  HOST: 127.0.0.1
  PORT: 3306
  USERNAME: root
  PASSWORD: '000000'
  QUERY:
```

and

**pythonic-project-samples/tests/settings.yml**
```yaml
DATABASE:
  DRIVER: mysql
  DATABASE: test
  HOST: 127.0.0.1
  PORT: 3306
  USERNAME: root
  PASSWORD: root
  QUERY:
```

</details>

3. Having the following app code:

<details>
<summary> Code </summary>

**pythonic-project-samples/src/example_blog/config/__init__.py**
```python
from pathlib import Path

from dynaconf import Dynaconf

_BASE_DIR = Path(__file__).parent.parent

settings = Dynaconf(
    envvar_prefix="EXAMPLE_BLOG",  # 环境变量前缀。设置`EXAMPLE_BLOG_FOO='bar'`，使用`settings.FOO`
    settings_files=[
        Path(__file__).parent / 'settings.yml',
    ],  # 指定绝对路径加载默认配置
    environments=False,  # 启用多层次日志，支持 dev, pro
    load_dotenv=True,  # 加载 .env
    env_switcher="EXAMPLE_BLOG_ENV",  # 用于切换模式的环境变量名称 EXAMPLE_BLOG_ENV=production
    lowercase_read=False,  # 禁用小写访问， settings.name 是不允许的
    # includes=['/etc/example_blog/settings.yaml'],  # 自定义配置覆盖默认配置
    base_dir=_BASE_DIR,  # 编码传入配置
)
```

**pythonic-project-samples/tests**

```python
from pathlib import Path

from example_blog.config import settings

settings.load_file(path=Path(__file__).parent / 'settings.yml')

print(settings.DATABASE)
```

I want to override the default configuration by loading the configuration in the test directory before running the test.

I knew I could use multiple levels of logging, but I didn't want to have a complex configuration file for operation and maintenance management.

</details>

4. Executing under the following environment

<details>
<summary> Execution </summary>

```bash
# virtualenv

python tests/__init__.py
```

</details>

**Expected behavior**
A clear and concise description of what you expected to happen.

```
Traceback (most recent call last):
  File "tests/__init__.py", line 5, in <module>
    settings.load_file(path=Path(__file__).parent / 'settings.yml')
  File "/home/kevin/.virtualenvs/pythonic-project-samples-Yf6zzgRF/lib/python3.7/site-packages/dynaconf/base.py", line 988, in load_file
    obj=self, name=_filename, silent=True
  File "/home/kevin/.virtualenvs/pythonic-project-samples-Yf6zzgRF/lib/python3.7/site-packages/dynaconf/loaders/py_loader.py", line 61, in try_to_load_from_py_module_name
    mod = importlib.import_module(name)
  File "/usr/lib/python3.7/importlib/__init__.py", line 118, in import_module
    if name.startswith('.'):
AttributeError: 'PosixPath' object has no attribute 'startswith'
```

**Environment (please complete the following information):**
 - OS: `Linux kevin 5.4.70-amd64-desktop #1 SMP Wed Oct 14 15:24:23 CST 2020 x86_64 GNU/Linux`
 - Dynaconf Version: `3.1.2`
 - Frameworks in use: `Fastapi`

**Additional context**

It's not that complicated. It's just that [`try_to_load_from_py_module_name`](https://github.com/rochacbruno/dynaconf/blob/c2d8b717486944ae6a42c70029f9c455c43a0809/dynaconf/loaders/py_loader.py#L44) didn't catch the exception object correctly while working with `pathlib.Path`.

https://github.com/rochacbruno/dynaconf/blob/c2d8b717486944ae6a42c70029f9c455c43a0809/dynaconf/loaders/py_loader.py#L44-L65


Discussion:

Resolving patch:
diff --git a/dynaconf/base.py b/dynaconf/base.py
--- a/dynaconf/base.py
+++ b/dynaconf/base.py
@@ -5,6 +5,7 @@
 import warnings
 from contextlib import contextmanager
 from contextlib import suppress
+from pathlib import Path
 
 from dynaconf import default_settings
 from dynaconf.loaders import default_loader
@@ -1001,9 +1002,19 @@ def load_file(self, path=None, env=None, silent=True, key=None):
                     # continue the loop.
                     continue
 
-                filepath = os.path.join(
-                    self._root_path or os.getcwd(), _filename
-                )
+                # python 3.6 does not resolve Pathlib basedirs
+                # issue #494
+                root_dir = str(self._root_path or os.getcwd())
+                if (
+                    isinstance(_filename, Path)
+                    and str(_filename.parent) in root_dir
+                ):  # pragma: no cover
+                    filepath = str(_filename)
+                else:
+                    filepath = os.path.join(
+                        self._root_path or os.getcwd(), str(_filename)
+                    )
+
                 paths = [
                     p
                     for p in sorted(glob.glob(filepath))
diff --git a/dynaconf/loaders/py_loader.py b/dynaconf/loaders/py_loader.py
--- a/dynaconf/loaders/py_loader.py
+++ b/dynaconf/loaders/py_loader.py
@@ -58,7 +58,7 @@ def try_to_load_from_py_module_name(
     ctx = suppress(ImportError, TypeError) if silent else suppress()
 
     with ctx:
-        mod = importlib.import_module(name)
+        mod = importlib.import_module(str(name))
         load_from_python_object(obj, mod, name, key, identifier)
         return True  # loaded ok!
     # if it reaches this point that means exception occurred, module not found.
diff --git a/example/issues/494_using_pathlib/app.py b/example/issues/494_using_pathlib/app.py
new file mode 100644
--- /dev/null
+++ b/example/issues/494_using_pathlib/app.py
@@ -0,0 +1,16 @@
+from pathlib import Path
+
+from dynaconf import Dynaconf
+
+base_dir = Path(__file__).parent / Path("folder")
+
+settings = Dynaconf(settings_files=[base_dir / "settings.yaml"])
+
+# __import__("pdbr").set_trace()
+settings.load_file(path=base_dir / Path("new.yaml"))
+
+print(settings._loaded_files)
+assert len(settings._loaded_files) == 2
+
+assert settings.NAME == "Booba"
+assert settings.NUMBER == 42


Test patch: