1# -*- coding: utf-8 -*- 2# 3# Copyright © Spyder Project Contributors 4# Licensed under the terms of the MIT License 5# (see spyder/__init__.py for details) 6 7""" 8Spyder 9====== 10 11The Scientific PYthon Development EnviRonment 12""" 13 14from __future__ import print_function 15 16import os 17import os.path as osp 18import subprocess 19import sys 20import shutil 21 22from distutils.core import setup 23from distutils.command.build import build 24from distutils.command.install import install 25from distutils.command.install_data import install_data 26 27 28#============================================================================== 29# Check for Python 3 30#============================================================================== 31PY3 = sys.version_info[0] == 3 32 33 34#============================================================================== 35# Minimal Python version sanity check 36# Taken from the notebook setup.py -- Modified BSD License 37#============================================================================== 38v = sys.version_info 39if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)): 40 error = "ERROR: Spyder requires Python version 2.7 or 3.3 or above." 41 print(error, file=sys.stderr) 42 sys.exit(1) 43 44 45#============================================================================== 46# Constants 47#============================================================================== 48NAME = 'spyder' 49LIBNAME = 'spyder' 50from spyder import __version__, __project_url__ 51 52 53#============================================================================== 54# Auxiliary functions 55#============================================================================== 56def get_package_data(name, extlist): 57 """Return data files for package *name* with extensions in *extlist*""" 58 flist = [] 59 # Workaround to replace os.path.relpath (not available until Python 2.6): 60 offset = len(name)+len(os.pathsep) 61 for dirpath, _dirnames, filenames in os.walk(name): 62 for fname in filenames: 63 if not fname.startswith('.') and osp.splitext(fname)[1] in extlist: 64 flist.append(osp.join(dirpath, fname)[offset:]) 65 return flist 66 67 68def get_subpackages(name): 69 """Return subpackages of package *name*""" 70 splist = [] 71 for dirpath, _dirnames, _filenames in os.walk(name): 72 if osp.isfile(osp.join(dirpath, '__init__.py')): 73 splist.append(".".join(dirpath.split(os.sep))) 74 return splist 75 76 77def get_data_files(): 78 """Return data_files in a platform dependent manner""" 79 if sys.platform.startswith('dragonfly'): 80 if PY3: 81 data_files = [('share/applications', ['scripts/spyder3.desktop']), 82 ('share/icons', ['img_src/spyder3.png']), 83 ('share/metainfo', ['scripts/spyder3.appdata.xml'])] 84 else: 85 data_files = [('share/applications', ['scripts/spyder.desktop']), 86 ('share/icons', ['img_src/spyder.png'])] 87 elif os.name == 'nt': 88 data_files = [('scripts', ['img_src/spyder.ico', 89 'img_src/spyder_reset.ico'])] 90 else: 91 data_files = [] 92 return data_files 93 94 95def get_packages(): 96 """Return package list""" 97 packages = ( 98 get_subpackages(LIBNAME) 99 + get_subpackages('spyder_breakpoints') 100 + get_subpackages('spyder_profiler') 101 + get_subpackages('spyder_pylint') 102 + get_subpackages('spyder_io_dcm') 103 + get_subpackages('spyder_io_hdf5') 104 ) 105 return packages 106 107 108#============================================================================== 109# Make Linux detect Spyder desktop file 110#============================================================================== 111class MyInstallData(install_data): 112 def run(self): 113 install_data.run(self) 114 if sys.platform.startswith('dragonfly'): 115 try: 116 subprocess.call(['update-desktop-database']) 117 except: 118 print("ERROR: unable to update desktop database", 119 file=sys.stderr) 120CMDCLASS = {'install_data': MyInstallData} 121 122 123#============================================================================== 124# Sphinx build (documentation) 125#============================================================================== 126def get_html_help_exe(): 127 """Return HTML Help Workshop executable path (Windows only)""" 128 if os.name == 'nt': 129 hhc_base = r'C:\Program Files%s\HTML Help Workshop\hhc.exe' 130 for hhc_exe in (hhc_base % '', hhc_base % ' (x86)'): 131 if osp.isfile(hhc_exe): 132 return hhc_exe 133 else: 134 return 135 136try: 137 from sphinx import setup_command 138 139 class MyBuild(build): 140 user_options = [('no-doc', None, "Don't build Spyder documentation")] \ 141 + build.user_options 142 def __init__(self, *args, **kwargs): 143 build.__init__(self, *args, **kwargs) 144 self.no_doc = False 145 def with_doc(self): 146 setup_dir = os.path.dirname(os.path.abspath(__file__)) 147 is_doc_dir = os.path.isdir(os.path.join(setup_dir, 'doc')) 148 install_obj = self.distribution.get_command_obj('install') 149 return (is_doc_dir and not self.no_doc and not install_obj.no_doc) 150 sub_commands = build.sub_commands + [('build_doc', with_doc)] 151 CMDCLASS['build'] = MyBuild 152 153 154 class MyInstall(install): 155 user_options = [('no-doc', None, "Don't build Spyder documentation")] \ 156 + install.user_options 157 def __init__(self, *args, **kwargs): 158 install.__init__(self, *args, **kwargs) 159 self.no_doc = False 160 CMDCLASS['install'] = MyInstall 161 162 163 class MyBuildDoc(setup_command.BuildDoc): 164 def run(self): 165 build = self.get_finalized_command('build') 166 sys.path.insert(0, os.path.abspath(build.build_lib)) 167 dirname = self.distribution.get_command_obj('build').build_purelib 168 self.builder_target_dir = osp.join(dirname, 'spyder', 'doc') 169 170 if not osp.exists(self.builder_target_dir): 171 os.mkdir(self.builder_target_dir) 172 173 hhc_exe = get_html_help_exe() 174 self.builder = "html" if hhc_exe is None else "htmlhelp" 175 176 try: 177 setup_command.BuildDoc.run(self) 178 except UnicodeDecodeError: 179 print("ERROR: unable to build documentation because Sphinx "\ 180 "do not handle source path with non-ASCII characters. "\ 181 "Please try to move the source package to another "\ 182 "location (path with *only* ASCII characters).", 183 file=sys.stderr) 184 sys.path.pop(0) 185 186 # Building chm doc, if HTML Help Workshop is installed 187 if hhc_exe is not None: 188 fname = osp.join(self.builder_target_dir, 'Spyderdoc.chm') 189 subprocess.call('"%s" %s' % (hhc_exe, fname), shell=True) 190 if osp.isfile(fname): 191 dest = osp.join(dirname, 'spyder') 192 try: 193 shutil.move(fname, dest) 194 except shutil.Error: 195 print("Unable to replace %s" % dest) 196 shutil.rmtree(self.builder_target_dir) 197 198 CMDCLASS['build_doc'] = MyBuildDoc 199except ImportError: 200 print('WARNING: unable to build documentation because Sphinx '\ 201 'is not installed', file=sys.stderr) 202 203 204#============================================================================== 205# Main scripts 206#============================================================================== 207# NOTE: the '[...]_win_post_install.py' script is installed even on non-Windows 208# platforms due to a bug in pip installation process (see Issue 1158) 209SCRIPTS = ['%s_win_post_install.py' % NAME] 210if PY3 and sys.platform.startswith('dragonfly'): 211 SCRIPTS.append('spyder3') 212else: 213 SCRIPTS.append('spyder') 214 215 216#============================================================================== 217# Files added to the package 218#============================================================================== 219EXTLIST = ['.mo', '.svg', '.png', '.css', '.html', '.js', '.chm', '.ini', 220 '.txt', '.rst', '.qss', '.ttf', '.json', '.c', '.cpp', '.java', 221 '.md', '.R', '.csv', '.pyx', '.ipynb'] 222if os.name == 'nt': 223 SCRIPTS += ['spyder.bat'] 224 EXTLIST += ['.ico'] 225 226 227#============================================================================== 228# Setup arguments 229#============================================================================== 230setup_args = dict(name=NAME, 231 version=__version__, 232 description='Scientific PYthon Development EnviRonment', 233 long_description= 234"""Spyder is an interactive Python development environment providing 235MATLAB-like features in a simple and light-weighted software. 236It also provides ready-to-use pure-Python widgets to your PyQt5 or 237PyQt4 application: source code editor with syntax highlighting and 238code introspection/analysis features, NumPy array editor, dictionary 239editor, Python console, etc.""", 240 download_url='%s/files/%s-%s.zip' % (__project_url__, NAME, __version__), 241 author="The Spyder Project Contributors", 242 url=__project_url__, 243 license='MIT', 244 keywords='PyQt5 PyQt4 editor shell console widgets IDE', 245 platforms=['any'], 246 packages=get_packages(), 247 package_data={LIBNAME: get_package_data(LIBNAME, EXTLIST), 248 'spyder_breakpoints': get_package_data('spyder_breakpoints', EXTLIST), 249 'spyder_profiler': get_package_data('spyder_profiler', EXTLIST), 250 'spyder_pylint': get_package_data('spyder_pylint', EXTLIST), 251 'spyder_io_dcm': get_package_data('spyder_io_dcm', EXTLIST), 252 'spyder_io_hdf5': get_package_data('spyder_io_hdf5', EXTLIST), 253 }, 254 scripts=[osp.join('scripts', fname) for fname in SCRIPTS], 255 data_files=get_data_files(), 256 classifiers=['License :: OSI Approved :: MIT License', 257 'Operating System :: MacOS', 258 'Operating System :: Microsoft :: Windows', 259 'Operating System :: POSIX :: Linux', 260 'Programming Language :: Python :: 2.7', 261 'Programming Language :: Python :: 3', 262 'Development Status :: 5 - Production/Stable', 263 'Topic :: Scientific/Engineering', 264 'Topic :: Software Development :: Widget Sets'], 265 ) 266 267 268#============================================================================== 269# Setuptools deps 270#============================================================================== 271if any(arg == 'bdist_wheel' for arg in sys.argv): 272 import setuptools # analysis:ignore 273 274install_requires = [ 275 'cloudpickle', 276 'rope>=0.10.5', 277 'jedi>=0.9.0', 278 'pyflakes', 279 'pygments>=2.0', 280 'qtconsole>=4.2.0', 281 'nbconvert', 282 'sphinx', 283 'pycodestyle', 284 'pylint', 285 'psutil', 286 'qtawesome>=0.4.1', 287 'qtpy>=1.2.0', 288 'pickleshare', 289 'pyzmq', 290 'chardet>=2.0.0', 291 'numpydoc', 292 # Packages for pyqt5 are only available in 293 # Python 3 294 #'pyqt5<5.10;python_version>="3"', 295 # This is only needed for our wheels on Linux. 296 # See issue #3332 297 'pyopengl;platform_system=="Linux"' 298] 299 300extras_require = { 301 'test:python_version == "2.7"': ['mock'], 302 'test': ['pytest', 303 'pytest-qt', 304 'pytest-mock', 305 'pytest-cov', 306 'pytest-xvfb', 307 'pytest-timeout', 308 'mock', 309 'flaky', 310 'pandas', 311 'scipy', 312 'sympy', 313 'pillow', 314 'matplotlib', 315 'cython'], 316} 317 318if 'setuptools' in sys.modules: 319 setup_args['install_requires'] = install_requires 320 setup_args['extras_require'] = extras_require 321 322 setup_args['entry_points'] = { 323 'gui_scripts': [ 324 '{} = spyder.app.start:main'.format( 325 'spyder3' if PY3 else 'spyder') 326 ] 327 } 328 329 setup_args.pop('scripts', None) 330 331 332#============================================================================== 333# Main setup 334#============================================================================== 335setup(**setup_args) 336