marcelotduarte/cx_Freeze

Django custom commands not found when built as .exe

dcabrero opened this issue · 1 comments

Describe the bug
When adding a Django custom admin command, it wont show or run by default when app is packaged.

To Reproduce
Start a basic boilerplate django app, then add a command:
https://docs.djangoproject.com/en/4.2/howto/custom-management-commands/

Expected behavior
After using setup.py build, should be able to run exename.exe custom_command

Desktop (please complete the following information):

  • Platform information: Windows 10 pro x64
  • Python 3.10.10
  • cx_Freeze version: 6.15.15
  • Django: 4.2.1

Additional context
I got it working by monkey patching this by importing this on my manage.py script.


def __override_commands():
    import django.utils.autoreload
    import django.core.management

    original_commands = django.core.management.get_commands()

    def _get_commands():

        original_commands
        commands = original_commands 
        commands['test_command'] = 'test_app'
        return commands

    _old_restart_with_reloader = django.utils.autoreload.restart_with_reloader

    def _restart_with_reloader(*args):
        import sys
        a0 = sys.argv.pop(0)
        try:
            return _old_restart_with_reloader(*args)
        finally:
            sys.argv.insert(0, a0)

    django.core.management.get_commands = _get_commands
    django.utils.autoreload.restart_with_reloader = _restart_with_reloader

__override_commands()
del __override_commands

And making sure that setup.py has the command.py file in its build_options:

build_options = {
    'packages': ['django', 'corsheaders', 'whitenoise', 'core_app', 'test_app'],
    'includes': ['test_app.management.commands.test_command'],
    'excludes': []
}

Hope it helps :]

Ok, after a few hours I noticed that even if were adding the package 'test_app', not all of its contents were going into the lib folder.

I'm now using this in my setup.py to get all the contents of a list of main folders that are part of a django project:

def get_py_files(directories):
    py_files = []
    for directory in directories:
        for root, dirs, files in os.walk(directory):
            for file in files:
                if file.endswith('.py'):
                    relative_root = root.replace(directory, '').lstrip(os.sep)
                    dotted_path = relative_root.replace(os.sep, '.')
                    if dotted_path:
                        py_files.append(f"{directory}.{dotted_path}.{file[:-3]}")
                    else:
                        py_files.append(f"{directory}.{file[:-3]}")
    return py_files 

Probably not the cleanest way, but it works and auto detects the command without the previous solution I did before.