def make_app_bundle(dist_dir, make_lite=False): """ Make macOS application bundle. Parameters ---------- dist_dir : pathlib.Path Directory in which to put the application bundle. make_lite : bool, optional Whether to create the application bundle with minimal packages. The default is False. """ from spyder.config.utils import EDIT_FILETYPES, _get_extensions build_type = 'lite' if make_lite else 'full' logger.info('Creating %s app bundle...', build_type) from packages import PACKAGES, INCLUDES, EXCLUDES, SCIENTIFIC if make_lite: EXCLUDES.extend(SCIENTIFIC) else: INCLUDES.extend(SCIENTIFIC) EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] OPTIONS = { 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': ICONFILE.as_posix(), 'dist_dir': dist_dir.as_posix(), 'emulate_shell_environment': True, 'plist': { 'CFBundleDocumentTypes': [{ 'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor' }], 'CFBundleIdentifier': 'org.spyder-ide.Spyder', 'CFBundleShortVersionString': SPYVER, 'NSRequiresAquaSystemAppearance': False, # Darkmode support 'LSEnvironment': { 'SPY_COMMIT': SPYCOM, 'SPY_BRANCH': SPYBRA } } } # Build the application setup(name=APP_BASE_NAME, app=[APPSCRIPT.as_posix()], options={'py2app': OPTIONS}) return
def make_app_bundle(dist_dir, make_lite=False): """ Make macOS application bundle. Parameters ---------- dist_dir : str Directory in which to put the application bundle. make_lite : bool, optional Whether to create the application bundle with minimal packages. The default is False. NOTES ----- py2app includes all packages in Spyder.app/Contents/Resources/lib/ python<ver>.zip, but some packages have issues when placed there. The following packages are included in py2app's PACKAGES option so that they will be placed in Spyder.app/Contents/Resources/lib/python<ver> instead. alabaster : Error message: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/alabaster' astroid : ImportError: cannot import name 'context' from 'astroid' (<path>/Resources/lib/python38.zip/astroid/__init__.pyc) ipykernel : ModuleNotFoundError: No module named 'ipykernel.datapub' ipython : [IPKernelApp] WARNING | Could not copy README_STARTUP to startup dir. Source file <path>/Resources/lib/python38.zip/IPython/core/profile/README_STARTUP does not exist jedi : jedi.api.environment.InvalidPythonEnvironment: Could not get version information for '<path>/Contents/MacOS/python': InternalError("The subprocess <path>/Contents/MacOS/python has crashed (EOFError('Ran out of input'), stderr=).") jinja2 : No module named 'jinja2.ext' keyring : ModuleNotFoundError: No module named 'keyring.backends.<mod>' parso : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/python38.zip/parso/python/grammar38.txt' PIL : Library not loaded: @loader_path/.dylibs/libjpeg.9.dylib Note: only applicable to not-Lite build pygments : ModuleNotFoundError: No module named 'pygments.formatters.latex' pyls : <path>/Contents/MacOS/python: No module named pyls Note: still occurs in alias mode qtawesome : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resourses/lib/ python38.zip/qtawesome/fonts/fontawesome4.7-webfont.ttf' spyder : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/spyder/app/mac_stylesheet.qss' spyder_kernels : No module named spyder_kernels.console.__main__ sphinx : No module named 'sphinx.builders.changes' """ build_type = 'lite' if make_lite else 'full' logger.info('Creating %s app bundle...', build_type) PACKAGES = ['alabaster', 'astroid', 'ipykernel', 'IPython', 'jedi', 'jinja2', 'keyring', 'parso', 'pygments', 'pyls', 'qtawesome', 'spyder', 'spyder_kernels', 'sphinx'] if make_lite: INCLUDES = [] EXCLUDES = [ 'numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy' ] else: INCLUDES = [ 'numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy' ] EXCLUDES = [] PACKAGES.append('PIL') EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] OPTIONS = { 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': ICONFILE, 'dist_dir': dist_dir, 'plist': { 'CFBundleDocumentTypes': [{'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor'}], 'CFBundleIdentifier': 'org.spyder-ide', 'CFBundleShortVersionString': SPYVER } } # Copy main application script app_script_name = MAC_APP_NAME.replace('.app', '.py') app_script_path = os.path.join(SPYREPO, 'scripts', app_script_name) shutil.copy2(os.path.join(SPYREPO, 'scripts', 'spyder'), app_script_path) try: setup(app=[app_script_path], options={'py2app': OPTIONS}) finally: os.remove(app_script_path) # Copy egg info from site-packages: fixes pkg_resources issue for pyls dest_dir = os.path.join(dist_dir, MAC_APP_NAME, 'Contents', 'Resources', 'lib', f'python{PYVER[0]}.{PYVER[1]}') for dist in pkg_resources.working_set: if dist.egg_info is None: continue dest = os.path.join(dest_dir, os.path.basename(dist.egg_info)) shutil.copytree(dist.egg_info, dest) logger.info('App bundle complete.') return
APP = [APP_MAIN_SCRIPT] DEPS = ['pylint', 'logilab', 'astroid', 'pycodestyle', 'setuptools'] EXCLUDES = DEPS + ['mercurial'] PACKAGES = [ 'spyder', 'spyder_breakpoints', 'spyder_io_dcm', 'spyder_io_hdf5', 'spyder_profiler', 'spyder_pylint', 'sphinx', 'jinja2', 'docutils', 'alabaster', 'babel', 'snowballstemmer', 'sphinx_rtd_theme', 'IPython', 'ipykernel', 'ipython_genutils', 'jupyter_client', 'jupyter_core', 'traitlets', 'qtconsole', 'pexpect', 'jedi', 'jsonschema', 'nbconvert', 'nbformat', 'qtpy', 'qtawesome', 'zmq', 'pygments', 'rope', 'distutils', 'PIL', 'PyQt5', 'sklearn', 'skimage', 'pandas', 'sympy', 'pyflakes', 'psutil', 'nose', 'patsy', 'statsmodels', 'seaborn', 'networkx' ] INCLUDES = get_stdlib_modules() EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] OPTIONS = { 'argv_emulation': True, 'compressed': False, 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': 'img_src/spyder.icns', 'plist': { 'CFBundleDocumentTypes': [{ 'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor' }],
def make_app_bundle(dist_dir, make_lite=False): """ Make macOS application bundle. Parameters ---------- dist_dir : str Directory in which to put the application bundle. make_lite : bool, optional Whether to create the application bundle with minimal packages. The default is False. NOTES ----- py2app includes all packages in Spyder.app/Contents/Resources/lib/ python<ver>.zip, but some packages have issues when placed there. The following packages are included in py2app's PACKAGES option so that they will be placed in Spyder.app/Contents/Resources/lib/python<ver> instead. alabaster : Error message: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/alabaster' astroid : ImportError: cannot import name 'context' from 'astroid' (<path>/Resources/lib/python38.zip/astroid/__init__.pyc) blib2to3 : File "<frozen zipimport>", line 177, in get_data KeyError: 'blib2to3/Users/rclary/Library/Caches/black/20.8b1/ Grammar3.8.6.final.0.pickle' jedi : jedi.api.environment.InvalidPythonEnvironment: Could not get version information for '<path>/Contents/MacOS/python': InternalError("The subprocess <path>/Contents/MacOS/python has crashed (EOFError('Ran out of input'), stderr=).") jinja2 : No module named 'jinja2.ext' keyring : ModuleNotFoundError: No module named 'keyring.backends.<mod>' pandas : From Variable explorer: KeyError('pandas._libs.interval') parso : jedi.api.environment.InvalidPythonEnvironment: Could not get version information for '/Users/rclary/opt/miniconda3/envs/c2w_37/bin/python': InternalError("The subprocess /Users/rclary/opt/miniconda3/envs/c2w_37/ bin/python has crashed (EOFError('Ran out of input'), stderr=).") PIL : Library not loaded: @loader_path/.dylibs/libjpeg.9.dylib Note: only applicable to not-Lite build pygments : ModuleNotFoundError: No module named 'pygments.formatters.latex' pyls : <path>/Contents/MacOS/python: No module named pyls Note: still occurs in alias mode pyls_black : Mandatory: pyls_black >=0.4.6 : None (NOK) pyls_spyder : Mandatory: pyls_spyder >=0.1.1 : None (NOK) qtawesome : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resourses/lib/ python38.zip/qtawesome/fonts/fontawesome4.7-webfont.ttf' setuptools : Mandatory: setuptools >=39.0.0 : None (NOK) sphinx : No module named 'sphinx.builders.changes' spyder : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/spyder/app/mac_stylesheet.qss' textdistance : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/ python39.zip/textdistance/libraries.json' """ import shutil import pkg_resources from spyder import __version__ as SPYVER from spyder.config.utils import EDIT_FILETYPES, _get_extensions from spyder.config.base import MAC_APP_NAME build_type = 'lite' if make_lite else 'full' logger.info('Creating %s app bundle...', build_type) PACKAGES = [ 'alabaster', 'astroid', 'blib2to3', 'jedi', 'jinja2', 'keyring', 'parso', 'pygments', 'pyls', 'pyls_black', 'pyls_spyder', 'qtawesome', 'setuptools', 'sphinx', 'spyder', 'textdistance', ] EXCLUDE_EGG = ['py2app'] if make_lite: INCLUDES = [] EXCLUDES = [ 'numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy', 'PIL' ] EXCLUDE_EGG.append('pillow') else: INCLUDES = [ 'numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy' ] EXCLUDES = [] PACKAGES.extend(['pandas', 'PIL']) EXCLUDE_EGG.extend(EXCLUDES) EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] FRAMEWORKS = [ '/usr/local/lib/libspatialindex.dylib', '/usr/local/lib/libspatialindex_c.dylib' ] # for rtree OPTIONS = { 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': ICONFILE, 'dist_dir': dist_dir, 'frameworks': FRAMEWORKS, 'plist': { 'CFBundleDocumentTypes': [{ 'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor' }], 'CFBundleIdentifier': 'org.spyder-ide', 'CFBundleShortVersionString': SPYVER } } # Copy main application script app_script_name = MAC_APP_NAME.replace('.app', '.py') app_script_path = os.path.join(SPYREPO, 'scripts', app_script_name) shutil.copy2(os.path.join(SPYREPO, 'scripts', 'spyder'), app_script_path) try: os.symlink(os.path.join(SPYREPO, 'spyder'), SPYLINK) setup(app=[app_script_path], options={'py2app': OPTIONS}) finally: os.remove(app_script_path) os.remove(SPYLINK) # Copy egg info from site-packages: fixes several pkg_resources issues dest_dir = os.path.join(dist_dir, MAC_APP_NAME, 'Contents', 'Resources', 'lib', f'python{PYVER[0]}.{PYVER[1]}') for dist in pkg_resources.working_set: if (dist.egg_info is None or dist.key.startswith('pyobjc') or dist.key in EXCLUDE_EGG): logger.info(f'Skipping egg {dist.key}') continue egg = os.path.basename(dist.egg_info) dest = os.path.join(dest_dir, egg) shutil.copytree(dist.egg_info, dest) logger.info(f'Copied {egg}') logger.info('App bundle complete.') return
APP = [APP_MAIN_SCRIPT] DEPS = ['pylint', 'logilab', 'astroid', 'pycodestyle', 'setuptools'] EXCLUDES = DEPS + ['mercurial'] PACKAGES = ['spyder', 'spyder_breakpoints', 'spyder_io_dcm', 'spyder_io_hdf5', 'spyder_profiler', 'spyder_pylint', 'sphinx', 'jinja2', 'docutils', 'alabaster', 'babel', 'snowballstemmer', 'sphinx_rtd_theme', 'IPython', 'ipykernel', 'ipython_genutils', 'jupyter_client', 'jupyter_core', 'traitlets', 'qtconsole', 'pexpect', 'jedi', 'jsonschema', 'nbconvert', 'nbformat', 'qtpy', 'qtawesome', 'zmq', 'pygments', 'rope', 'distutils', 'PIL', 'PyQt5', 'sklearn', 'skimage', 'pandas', 'sympy', 'pyflakes', 'psutil', 'nose', 'patsy','statsmodels', 'seaborn', 'networkx'] INCLUDES = get_stdlib_modules() EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] OPTIONS = { 'argv_emulation': True, 'compressed' : False, 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': 'img_src/spyder.icns', 'plist': {'CFBundleDocumentTypes': [{'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor'}], 'CFBundleIdentifier': 'org.spyder-ide', 'CFBundleShortVersionString': spy_version} }
def make_app_bundle(dist_dir, make_lite=False): """ Make macOS application bundle. Parameters ---------- dist_dir : str Directory in which to put the application bundle. make_lite : bool, optional Whether to create the application bundle with minimal packages. The default is False. NOTES ----- py2app includes all packages in Spyder.app/Contents/Resources/lib/ python<ver>.zip, but some packages have issues when placed there. The following packages are included in py2app's PACKAGES option so that they will be placed in Spyder.app/Contents/Resources/lib/python<ver> instead. alabaster : Error message: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/alabaster' astroid : ImportError: cannot import name 'context' from 'astroid' (<path>/Resources/lib/python38.zip/astroid/__init__.pyc) blib2to3 : File "<frozen zipimport>", line 177, in get_data KeyError: 'blib2to3/Users/rclary/Library/Caches/black/20.8b1/ Grammar3.8.6.final.0.pickle' docutils : [Errno 20] Not a directory: '<path>/Resources/lib/python39.zip/ docutils/writers/latex2e/docutils.sty' IPython : [IPKernelApp] WARNING | Could not copy README_STARTUP to startup dir. Source file <path>/Resources/lib/python38.zip/IPython/core/profile/README_STARTUP does not exist jedi : jedi.api.environment.InvalidPythonEnvironment: Could not get version information for '<path>/Contents/MacOS/python': InternalError("The subprocess <path>/Contents/MacOS/python has crashed (EOFError('Ran out of input'), stderr=).") jinja2 : No module named 'jinja2.ext' keyring : ModuleNotFoundError: No module named 'keyring.backends.<mod>' pandas : From Variable explorer: KeyError('pandas._libs.interval') parso : jedi.api.environment.InvalidPythonEnvironment: Could not get version information for '/Users/rclary/opt/miniconda3/envs/c2w_37/bin/python': InternalError("The subprocess /Users/rclary/opt/miniconda3/envs/c2w_37/ bin/python has crashed (EOFError('Ran out of input'), stderr=).") PIL : Library not loaded: @loader_path/.dylibs/libjpeg.9.dylib Note: only applicable to not-Lite build pygments : ModuleNotFoundError: No module named 'pygments.formatters.latex' pylint : <path>/Contents/MacOS/python: No module named pylint.__main__ pylsp : <path>/Contents/MacOS/python: No module named pylsp Note: still occurs in alias mode pylsp_black : Mandatory: python-pyls-black >=1.0.0 : None (NOK) pyls_spyder : Mandatory: pyls_spyder >=0.1.1 : None (NOK) qtawesome : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resourses/lib/ python38.zip/qtawesome/fonts/fontawesome4.7-webfont.ttf' setuptools : Mandatory: setuptools >=49.6.0 : None (NOK) sphinx : No module named 'sphinx.builders.changes' spyder : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/ python38.zip/spyder/app/mac_stylesheet.qss' spyder_kernels : No module named spyder_kernels.console.__main__ textdistance : NotADirectoryError: [Errno 20] Not a directory: '<path>/Resources/lib/ python39.zip/textdistance/libraries.json' """ import shutil import pkg_resources from spyder import __version__ as SPYVER from spyder.config.utils import EDIT_FILETYPES, _get_extensions from spyder.config.base import MAC_APP_NAME # Patch py2app for IPython help() py2app_file = pkg_resources.pkgutil.get_loader('py2app').get_filename() site_file = os.path.join(os.path.dirname(py2app_file), 'apptemplate', 'lib', 'site.py') logger.info('Patching %s...', site_file) with open(site_file, 'a+') as f: f.seek(0) content = f.read() if 'builtins.help = _sitebuiltins._Helper()' not in content: f.write('\nimport builtins' '\nimport _sitebuiltins' '\nbuiltins.help = _sitebuiltins._Helper()\n') build_type = 'lite' if make_lite else 'full' logger.info('Creating %s app bundle...', build_type) PACKAGES = [ 'alabaster', 'astroid', 'blib2to3', 'docutils', 'IPython', 'jedi', 'jinja2', 'keyring', 'parso', 'pygments', 'pylint', 'pylsp', 'pylsp_black', 'pyls_spyder', 'qtawesome', 'setuptools', 'sphinx', 'spyder', 'spyder_kernels', 'textdistance', ] INCLUDES = [ '_sitebuiltins', # required for IPython help() # required for sphinx 'sphinxcontrib.applehelp', 'sphinxcontrib.devhelp', 'sphinxcontrib.htmlhelp', 'sphinxcontrib.jsmath', 'sphinxcontrib.qthelp', 'sphinxcontrib.serializinghtml' ] EXCLUDES = [] EXCLUDE_EGG = ['py2app'] if make_lite: EXCLUDES.extend([ 'numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy', 'PIL' ]) EXCLUDE_EGG.extend(['pillow']) else: INCLUDES.extend( ['numpy', 'scipy', 'pandas', 'matplotlib', 'cython', 'sympy']) PACKAGES.extend(['pandas', 'PIL']) EXCLUDE_EGG.extend(EXCLUDES) EDIT_EXT = [ext[1:] for ext in _get_extensions(EDIT_FILETYPES)] # Get rtree dylibs rtree_loc = pkg_resources.get_distribution('rtree').module_path rtree_dylibs = os.scandir(os.path.join(rtree_loc, 'rtree', 'lib')) FRAMEWORKS = [lib.path for lib in rtree_dylibs] OPTIONS = { 'optimize': 0, 'packages': PACKAGES, 'includes': INCLUDES, 'excludes': EXCLUDES, 'iconfile': ICONFILE, 'dist_dir': dist_dir, 'frameworks': FRAMEWORKS, 'plist': { 'CFBundleDocumentTypes': [{ 'CFBundleTypeExtensions': EDIT_EXT, 'CFBundleTypeName': 'Text File', 'CFBundleTypeRole': 'Editor' }], 'CFBundleIdentifier': 'org.spyder-ide', 'CFBundleShortVersionString': SPYVER, 'NSRequiresAquaSystemAppearance': False # Darkmode support } } # Copy main application script app_script_name = MAC_APP_NAME.replace('.app', '.py') app_script_path = os.path.join(SPYREPO, 'scripts', app_script_name) shutil.copy2(os.path.join(SPYREPO, 'scripts', 'spyder'), app_script_path) # Build the application try: os.symlink(os.path.join(SPYREPO, 'spyder'), SPYLINK) setup(app=[app_script_path], options={'py2app': OPTIONS}) finally: os.remove(app_script_path) os.remove(SPYLINK) # Copy egg info from site-packages: fixes several pkg_resources issues dest_dir = os.path.join(dist_dir, MAC_APP_NAME, 'Contents', 'Resources', 'lib', f'python{PYVER[0]}.{PYVER[1]}') pkg_resources.working_set.add_entry(SPYREPO) for dist in pkg_resources.working_set: if (dist.egg_info is None or dist.key.startswith('pyobjc') or dist.key in EXCLUDE_EGG): logger.info(f'Skipping egg {dist.key}') continue egg = os.path.basename(dist.egg_info) dest = os.path.join(dest_dir, egg) shutil.copytree(dist.egg_info, dest) logger.info(f'Copied {egg}') logger.info('App bundle complete.') return