Skip to content

Built datashuttle executable with PyInstaller#535

Merged
JoeZiminski merged 10 commits into
neuroinformatics-unit:cross-platform-packagingfrom
sumana-2705:packaging_project
Sep 2, 2025
Merged

Built datashuttle executable with PyInstaller#535
JoeZiminski merged 10 commits into
neuroinformatics-unit:cross-platform-packagingfrom
sumana-2705:packaging_project

Conversation

@sumana-2705
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

@JoeZiminski JoeZiminski changed the base branch from main to cross-platform-packaging September 2, 2025 18:06
@JoeZiminski JoeZiminski merged commit 9ae5ded into neuroinformatics-unit:cross-platform-packaging Sep 2, 2025
6 of 9 checks passed
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