diff --git a/.github/workflows/build_pip.yml b/.github/workflows/build_pip.yml index 4f2f69177..8a4ee55f8 100644 --- a/.github/workflows/build_pip.yml +++ b/.github/workflows/build_pip.yml @@ -38,7 +38,7 @@ jobs: strategy: matrix: os: [windows-latest, macos-latest] #, ubuntu-latest] - python: ['3.9',] # '3.10', '3.11', '3.12', '3.13'] + python: ['3.9', '3.10', '3.11', '3.12', '3.13'] arch: [x86_64, arm64] exclude: - os: windows-latest diff --git a/PYME/Acquire/acquire_app.py b/PYME/Acquire/acquire_app.py new file mode 100644 index 000000000..275f24365 --- /dev/null +++ b/PYME/Acquire/acquire_app.py @@ -0,0 +1,14 @@ +"""PYME Acquire application entry point script.""" + +from PYME.util.uilaunch import ensure_macos_framework_build + +def main(): + # make sure we have a framework build on macOS so that ui elements work properly + # run this before importing any ui elements (or anything that might take time to import) + ensure_macos_framework_build() + + from PYME.Acquire import PYMEAcquire + PYMEAcquire.main() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/PYME/DSView/PYMEImage.py b/PYME/DSView/PYMEImage.py new file mode 100644 index 000000000..22353b50a --- /dev/null +++ b/PYME/DSView/PYMEImage.py @@ -0,0 +1,18 @@ +""" +Entry point for PYMEImage application + +Minimal stub to ensure that on macOS we have a framework build of python +""" + +from PYME.util.uilaunch import ensure_macos_framework_build + +def main(): + # make sure we have a framework build on macOS so that ui elements work properly + # run this before importing any ui elements (or anything that might take time to import) + ensure_macos_framework_build() + + from PYME.DSView import dsviewer + dsviewer.main() + +if __name__ == '__main__': + main() diff --git a/PYME/LMVis/PYMEVisualise.py b/PYME/LMVis/PYMEVisualise.py new file mode 100644 index 000000000..e7e4ccf25 --- /dev/null +++ b/PYME/LMVis/PYMEVisualise.py @@ -0,0 +1,16 @@ +""" +Entry point module for PYMEVisualise application +""" + +from PYME.util.uilaunch import ensure_macos_framework_build + +def main(): + # make sure we have a framework build on macOS so that ui elements work properly + # run this before importing any ui elements (or anything that might take time to import) + ensure_macos_framework_build() + + from PYME.LMVis import VisGUI + VisGUI.main() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/PYME/cluster/cluster_of_one.py b/PYME/cluster/cluster_of_one.py index db915218e..12cf485c0 100644 --- a/PYME/cluster/cluster_of_one.py +++ b/PYME/cluster/cluster_of_one.py @@ -5,6 +5,8 @@ import os import webbrowser +from PYME.util.uilaunch import ensure_macos_framework_build + import logging logger = logging.getLogger(__name__) @@ -121,6 +123,8 @@ def run(self): def main(): + ensure_macos_framework_build() + logging.basicConfig(level=logging.DEBUG) import wx import PYME.resources diff --git a/PYME/recipes/bakeshop_app.py b/PYME/recipes/bakeshop_app.py new file mode 100644 index 000000000..5801a35a4 --- /dev/null +++ b/PYME/recipes/bakeshop_app.py @@ -0,0 +1,16 @@ +""" +entrypoint for bakeshop +""" + +from PYME.util.uilaunch import ensure_macos_framework_build + +def main(): + # make sure we have a framework build on macOS so that ui elements work properly + # run this before importing any ui elements (or anything that might take time to import) + ensure_macos_framework_build() + + from PYME.recipes.bakeshop import bakeshop_app + bakeshop_app.main() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/PYME/update_version.py b/PYME/update_version.py index 8dfc69c69..dc605285e 100644 --- a/PYME/update_version.py +++ b/PYME/update_version.py @@ -149,7 +149,7 @@ def update_version(): s = f.read() s = re.sub(r'version\s*=\s*".*?"', 'version = "%s"' % new_version, s) - print('Updating version in %s to %s' % (fn, new_version)) + #print('Updating version in %s to %s' % (fn, new_version)) with open(fn, 'w') as f: f.write(s) diff --git a/PYME/util/uilaunch.py b/PYME/util/uilaunch.py new file mode 100644 index 000000000..df0603390 --- /dev/null +++ b/PYME/util/uilaunch.py @@ -0,0 +1,39 @@ +import sys +import os +import shutil + +def ensure_macos_framework_build(): + """ + Ensures the script is running with a Framework build of Python (python.app) + on macOS. If not, it attempts to find one and relaunches the process. + """ + if sys.platform != "darwin": + return + + # If we are already running inside a .app bundle (standard Framework build) + # or the specific python.app executable, we are good. + if ".app/Contents/MacOS/python" in sys.executable: + return + + current_exe = sys.executable + env_base = sys.prefix # The root of the current environment (venv or conda env) + + + # 1. Look for Conda's specific python.app (The most common need for this) + # Structure: /python.app/Contents/MacOS/python + conda_app = os.path.join(env_base, "python.app", "Contents", "MacOS", "python") + + if os.path.exists(conda_app) and os.access(conda_app, os.X_OK): + target_python = conda_app + else: + target_python = None + print("Warning: Could not find a python.app executable in the current conda environment.") + return + + + # 4. The Relaunch + # Only relaunch if we found a better python AND we aren't already running it. + if target_python and os.path.abspath(current_exe) != os.path.abspath(target_python): + # We use execv to replace the current process (keeping PID same-ish) + # We pass the new executable as argv[0] and all original args + os.execv(target_python, [target_python] + sys.argv) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 8796b43be..38f64278e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,5 +57,28 @@ dependencies = [ ] +[project.scripts] +dh5view = "PYME.DSView.PYMEImage:main" +PYMEImage = "PYME.DSView.PYMEImage:main" +PYMEAcquire = "PYME.Acquire.PYMEAcquire:main" +VisGUI = "PYME.LMVis.PYMEVisualise:main" +PYMEVis = "PYME.LMVis.PYMEVisualise:main" +PYMEVisualize = "PYME.LMVis.PYMEVisualise:main" +PYMEVisualise = "PYME.LMVis.PYMEVisualise:main" +PYMEBatch = "PYME.recipes.batchProcess:main" +bakeshop = "PYME.recipes.bakeshop_app:main" +PYMEDataServer = "PYME.cluster.HTTPDataServer:main" +PYMEClusterDup = "PYME.io.clusterDuplication:main" +PYMERuleServer = "PYME.cluster.PYMERuleServer:main" +PYMENodeServer = "PYME.cluster.PYMENodeServer:main" +PYMERuleNodeServer = "PYME.cluster.PYMERuleNodeServer:main" +PYMEClusterOfOne = "PYME.cluster.cluster_of_one:main" +#PYMEWebDav = "PYME.cluster.webdav:main" +PYMEscmosmapgen = "PYME.Analysis.gen_sCMOS_maps:main" +#fitMonP = "PYME.ParallelTasks.fitMonP:main" +#taskWorkerZC = "PYME.ParallelTasks.taskWorkerZC:main" +#taskServerZC = "PYME.ParallelTasks.taskServerZC:main" +#slaunchWorkers = "PYME.ParallelTasks.launchWorkers:main" + [project.urls] -"Homepage" = "https://github.com/python-microscopy/pymecompress" +"Homepage" = "https://www.python-microscopy.org"