Skip to content

Built datashuttle executable with PyInstaller #535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

sumana-2705
Copy link
Contributor

Description

What is this PR

  • Bug fix
  • Addition of a new feature
  • Other

Why is this PR needed?

This PR demonstrates the process of building the datashuttle package as a standalone executable using PyInstaller. It allows maintainers to review and verify the feasibility of distributing datashuttle as a packaged binary, simplifying installation and usage for end users.

What does this PR do?

  • Builds the datashuttle package using PyInstaller with tui_launcher.py as the entry point.
  • Adds the generated executable (datashuttle.exe) for demonstration purposes only.
  • Shows how datashuttle can function independently of a Python environment, providing a potential path for binary distribution.

References

Closes #517

Is this a breaking change?

No. This PR only adds a demonstration build artifact and does not modify existing source code or APIs.

Does this PR require an update to the documentation?

No immediate documentation updates are required. However, if binary distribution is adopted in the future, documentation changes should describe installation and usage of the executable.

@JoeZiminski
Copy link
Member

Hey @sumana-2705, thanks for this! This is very cool. Could you also push the pyinstaller spec file and actually, delete the executable. Apologies I did not say, I will add the build and dist files to the gitignore, and devs can build the .exe file locally themselves using pyinstaller.

@JoeZiminski
Copy link
Member

Hey @sumana-2705 thanks for this! I saw the same error you had, it is annoying because if you double-click the executable the console is deleted as soon as it finishes. To get around this, you can open a terminal directly, cd to the dir where the exe is and run with datashutle.exe launch.

There were two changes to make it work, one was to add the lines;

    hiddenimports=[
        'datashuttle.tui_launcher',
        'datashuttle.tui.app',
        'textual.widgets._tab_pane',
        'textual.widgets._input',
        'textual.widgets._tree_control',
    ],

sometimes pyinstaller cannot find all of the requirements from the imports at the top of the file, and these need to be specified directly.

This worked, but then the tcss did not render properly, it turns out the files needs to be copied, with the following code added:

# Include .tcss files
tcss_files = [
   (f, os.path.join("datashuttle", "tui", "css"))
   for f in glob("../datashuttle/tui/css/*.tcss")
]

and

    datas=tcss_files,

Honestly, ChatGPT if very useful for working with pyinstaller in case you find it useful, I've used it a lot before and its a real pain to dig deep into the documentation to find these weird errors!

There are a couple of other things we can do next:

  1. There is an error because the Rclone dependency cannot be installed. Instead, this can be installed at the top of the file:
# Get current conda environment prefix
env_prefix = sys.prefix

# Detect OS and set rclone path
if platform.system() == "Windows":
    rclone_src = os.path.join(env_prefix, "bin", "rclone.exe")
else:
    rclone_src = os.path.join(env_prefix, "bin", "rclone")

# Verify rclone exists
if not os.path.isfile(rclone_src):
    raise FileNotFoundError(f"rclone binary not found at: {rclone_src}")

# Add rclone as a binary to be bundled
binaries = [(rclone_src, '.')]

and add binaries=binaries below.

This should then copy your rclone from conda (which we can use to manage the install cross platform) to the install dir.

  1. We can also move the spec file to a top level folder called package and instead of using tui_launcher.py we can compartmentalize a short script so we can do this cross platform and avoid having to write launch at the end. We could run with:
import sys
from pathlib import Path

# Add project root to sys.path
project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root))

# Import main from tui_launcher
from datashuttle.tui_launcher import main as datashuttle_main

def run():
    # Simulate: datashuttle launch
    sys.argv = ["datashuttle", "launch"]
    datashuttle_main()

if __name__ == "__main__":
    try:
        run()
    except Exception as e:
        print(f"\nError: {e}")
    finally:
        input("\nPress Enter to exit...")

Let me know if you'd like to play around with these and see if this works on your machine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Cross-Platform Executable Deployment for datashuttle Terminal User Interface
2 participants