I am building a pex bundle of a Django site as follows:
$ pipenv-pex --entry-point "manage.main"
The resulting project.pex
runs as expected for 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]
...
But if I try to actually run the server, I get a NotADirectoryError (traceback below). What directory is execute_from_command_line
looking for (it seems to be finding the __main__
method in the pex file?) and how can I correctly invoke it?
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 is a demo/development webserver that automatically reloads Python code. One of its features is reloading Python code, so that you can make changes without needing to restarting your local development instance. This feature wasn't really designed with the context of pex file layout in mind.
You can work around this problem by disabling the reload functionality:
./project.pex runserver --noreload
..at which point you will likely run into issues loading files relative to 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
You can work around these problems by updating how BASE_DIR
is calculated in settings.py
:
# 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)
In general, the process believes it is running in a regular folder, which is not the case. Further workarounds may be required.