在 Django 站点的 pex 上调用 runserver 时如何解决 NotADirectoryError?

How can I resolve NotADirectoryError when calling runserver on pex of a Django site?

我正在构建一个 pex bundle Django 站点,如下所示:

$ pipenv-pex --entry-point "manage.main"

结果 project.pex 运行s 符合 runserver --help 的预期:

$ ./project.pex runserver --help
usage: toptal_joglog.pex runserver [-h] [--ipv6] [--nothreading] [--noreload]
                                   [--nostatic] [--insecure] [--version]
                                   [-v {0,1,2,3}] [--settings SETTINGS]
...

但是如果我尝试 运行 服务器,我会得到一个 NotADirectoryError(下面的回溯)。 execute_from_command_line 正在寻找什么目录(似乎是在 pex 文件中找到 __main__ 方法?)以及如何正确调用它?

Watching for file changes with StatReloader
Performing system checks...

Traceback (most recent call last):
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 396, in execute
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 328, in _wrap_coverage
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 359, in _wrap_profiling
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 447, in _execute
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 544, in execute_entry
  File "/home/user/Documents/project/project.pex/.bootstrap/pex/pex.py", line 559, in execute_pkg_resources
  File "/home/user/Documents/project/project.pex/manage.py", line 18, in main
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/__init__.py", line 395, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/base.py", line 328, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/commands/runserver.py", line 60, in execute
    super().execute(*args, **options)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/base.py", line 369, in execute
    output = self.handle(*args, **options)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/commands/runserver.py", line 95, in handle
    self.run(**options)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/core/management/commands/runserver.py", line 102, in run
    autoreload.run_with_reloader(self.inner_run, **options)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 599, in run_with_reloader
    start_django(reloader, main_func, *args, **kwargs)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 584, in start_django
    reloader.run(django_main_thread)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 299, in run
    self.run_loop()
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 305, in run_loop
    next(ticker)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 345, in tick
    for filepath, mtime in self.snapshot_files():
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 361, in snapshot_files
    for file in self.watched_files():
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 260, in watched_files
    yield from iter_all_python_module_files()
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 105, in iter_all_python_module_files
    return iter_modules_and_files(modules, frozenset(_error_files))
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/autoreload.py", line 141, in iter_modules_and_files
    resolved_path = path.resolve(strict=True).absolute()
  File "/usr/lib/python3.7/pathlib.py", line 1161, in resolve
    s = self._flavour.resolve(self, strict=strict)
  File "/usr/lib/python3.7/pathlib.py", line 361, in resolve
    return _resolve(base, str(path)) or sep
  File "/usr/lib/python3.7/pathlib.py", line 345, in _resolve
    target = accessor.readlink(newpath)
  File "/usr/lib/python3.7/pathlib.py", line 443, in readlink
    return os.readlink(path)
NotADirectoryError: [Errno 20] Not a directory: '/home/user/Documents/project/project.pex/__main__.py'

runserver 是一个 demo/development 网络服务器,可以自动重新加载 Python 代码。它的一项功能是重新加载 Python 代码,这样您就可以进行更改而无需重新启动本地开发实例。此功能在设计时并未真正考虑到 pex 文件布局的上下文。

您可以通过禁用重新加载功能来解决此问题:

./project.pex runserver --noreload

..在这一点上你可能会 运行 加载相对于 BASE_DIR:

的文件
System check identified no issues (0 silenced).
Traceback (most recent call last):
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/db/backends/base/base.py", line 220, in ensure_connection
    self.connect()
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/db/backends/base/base.py", line 197, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/home/user/.pex/installed_wheels/76e2a7db05161ac57adcee837113974bb4f07b41/Django-3.0.6-py3-none-any.whl/django/db/backends/sqlite3/base.py", line 199, in get_new_connection
    conn = Database.connect(**conn_params)
sqlite3.OperationalError: unable to open database file

您可以通过更新 settings.pyBASE_DIR 的计算方式来解决这些问题:

# when running from source
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# when running from pex
while os.path.isfile(BASE_DIR):
   BASE_DIR = os.path.dirname(BASE_DIR)

一般情况下,该进程认为它 运行ning 在常规文件夹中,但事实并非如此。可能需要进一步的解决方法。