[FIX] Fix urlopen indicating the timeout parameter
[cds-indico.git] / setup.py
bloba94361ed236ed28f871b66818c10d9e88cf4834e
1 # -*- coding: utf-8 -*-
2 ##
3 ##
4 ## This file is part of CDS Indico.
5 ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6 ##
7 ## CDS Indico is free software; you can redistribute it and/or
8 ## modify it under the terms of the GNU General Public License as
9 ## published by the Free Software Foundation; either version 2 of the
10 ## License, or (at your option) any later version.
12 ## CDS Indico is distributed in the hope that it will be useful, but
13 ## WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ## General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 # Autoinstalls setuptools if the user doesn't have them already
22 import ez_setup
23 ez_setup.use_setuptools()
25 import commands
26 import getopt
27 import os
28 import re
29 import shutil
30 import string
31 import sys
32 from distutils.sysconfig import get_python_lib, get_python_version
33 from distutils.cmd import Command
34 from distutils.command import bdist
35 from indico.util import i18n
38 import pkg_resources
39 from setuptools.command import develop, install, sdist, bdist_egg, easy_install
40 from setuptools import setup, find_packages, findall
43 try:
44 from babel.messages import frontend as babel
45 BABEL_PRESENT = True
46 except ImportError:
47 BABEL_PRESENT = False
50 EXTRA_RESOURCES_URL = "http://indico-software.org/wiki/Admin/Installation/IndicoExtras"
52 if sys.platform == 'linux2':
53 import pwd
54 import grp
57 class vars(object):
58 '''Variable holder.'''
59 packageDir = None
60 versionVal = 'None'
61 accessuser = None
62 accessgroup = None
63 dbInstalledBySetupPy = False
64 binDir = None
65 documentationDir = None
66 configurationDir = None
67 htdocsDir = None
69 ### Methods required by setup() ##############################################
71 def _generateDataPaths(x):
73 dataFilesDict = {}
75 for (baseDstDir, files, remove_first_x_chars) in x:
76 for f in files:
77 dst_dir = os.path.join(baseDstDir, os.path.dirname(f)[remove_first_x_chars:])
78 if dst_dir not in dataFilesDict:
79 dataFilesDict[dst_dir] = []
80 dataFilesDict[dst_dir].append(f)
82 dataFiles = []
83 for k, v in dataFilesDict.items():
84 dataFiles.append((k, v))
86 return dataFiles
88 def _getDataFiles(x):
89 """
90 Returns a fully populated data_files ready to be fed to setup()
92 WARNING: when creating a bdist_egg we need to include files inside bin,
93 doc, config & htdocs into the egg therefore we cannot fetch indico.conf
94 values directly because they will not refer to the proper place. We
95 include those files in the egg's root folder.
96 """
98 # setup expects a list like this (('foo/bar/baz', 'wiki.py'),
99 # ('a/b/c', 'd.jpg'))
101 # What we do below is transform a list like this:
102 # (('foo', 'bar/baz/wiki.py'),
103 # ('a', 'b/c/d.jpg'))
105 # first into a dict and then into a pallatable form for setuptools.
107 # This re will be used to filter out etc/*.conf files and therefore not overwritting them
108 isAConfRe = re.compile('etc\/[^/]+\.conf$')
110 dataFiles = _generateDataPaths((('bin', findall('bin'), 4),
111 ('doc', findall('doc'), 4),
112 ('etc', [xx for xx in findall('etc') if not isAConfRe.search(xx)], 4),
113 ('htdocs', findall('indico/htdocs'), 14)))
114 return dataFiles
120 def _getInstallRequires():
121 '''Returns external packages required by Indico
123 These are the ones needed for runtime.'''
125 base = ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface',
126 'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0',
127 'pypdf', 'mako>=0.4.1', 'babel', 'icalendar', 'pyatom', 'python-memcached']
129 #for Python older than 2.7
130 if sys.version_info[0] <= 2 and sys.version_info[1] < 7:
131 base.append('argparse')
133 return base
136 def _versionInit():
137 '''Retrieves the version number from indico/MaKaC/__init__.py and returns it'''
139 from indico.MaKaC import __version__
140 v = __version__
142 print('Indico %s' % v)
144 return v
146 ### Commands ###########################################################
147 class sdist_indico(sdist.sdist):
148 user_options = sdist.sdist.user_options + \
149 [('version=', None, 'version to distribute')]
150 version = 'dev'
152 def run(self):
153 global x
154 sdist.sdist.run(self)
157 class jsdist_indico:
158 def jsCompress(self):
159 from MaKaC.consoleScripts.installBase import jsCompress
160 jsCompress()
161 self.dataFiles += _generateDataPaths([
162 ('htdocs/js/presentation/pack', findall('indico/htdocs/js/presentation/pack'), 35),
163 ('htdocs/js/indico/pack', findall('indico/htdocs/js/indico/pack'), 29),
164 ('htdocs/js/livesync/pack', findall('indico/htdocs/js/indico/pack'), 31),
165 ('htdocs/js/jquery/pack', findall('indico/htdocs/js/indico/pack'), 29)])
168 def _bdist_indico(dataFiles):
169 class bdist_indico(bdist.bdist, jsdist_indico):
170 def run(self):
171 self.jsCompress()
172 compileAllLanguages(self)
173 bdist.bdist.run(self)
175 bdist_indico.dataFiles = dataFiles
176 return bdist_indico
179 def _bdist_egg_indico(dataFiles):
180 class bdist_egg_indico(bdist_egg.bdist_egg, jsdist_indico):
181 def run(self):
182 self.jsCompress()
183 compileAllLanguages(self)
184 bdist_egg.bdist_egg.run(self)
186 bdist_egg_indico.dataFiles = dataFiles
187 return bdist_egg_indico
190 class jsbuild(Command):
191 description = "minifies and packs javascript files"
192 user_options = []
193 boolean_options = []
195 def initialize_options(self):
196 pass
198 def finalize_options(self):
199 pass
201 def run(self):
202 from MaKaC.consoleScripts.installBase import jsCompress
203 jsCompress()
205 class fetchdeps:
206 def run(self):
207 print "Checking if dependencies need to be installed..."
209 wset = pkg_resources.working_set
211 wset.resolve(map(pkg_resources.Requirement.parse,
212 filter(lambda x: x != None, _getInstallRequires())),
213 installer=self._installMissing)
215 print "Done!"
218 def _installMissing(self, dist):
219 env = pkg_resources.Environment()
220 print dist, EXTRA_RESOURCES_URL
221 easy_install.main(["-f", EXTRA_RESOURCES_URL, "-U", str(dist)])
222 env.scan()
224 if env[str(dist)]:
225 return env[str(dist)][0]
226 else:
227 return None
230 class fetchdeps_indico(fetchdeps, Command):
231 description = "fetch all the dependencies needed to run Indico"
232 user_options = []
233 boolean_options = []
235 def initialize_options(self):
236 pass
238 def finalize_options(self):
239 pass
242 class develop_indico(Command):
243 description = "prepares the current directory for Indico development"
244 user_options = []
245 boolean_options = []
247 def initialize_options(self):
248 pass
250 def finalize_options(self):
251 pass
253 def run(self):
255 fetchdeps().run()
257 local = 'etc/indico.conf'
258 if os.path.exists(local):
259 print 'Upgrading existing etc/indico.conf..'
260 upgrade_indico_conf(local, 'etc/indico.conf.sample')
261 else:
262 print 'Creating new etc/indico.conf..'
263 shutil.copy('etc/indico.conf.sample', local)
265 for f in [x for x in ('etc/zdctl.conf', 'etc/zodb.conf', 'etc/logging.conf') if not os.path.exists(x)]:
266 shutil.copy('%s.sample' % f, f)
268 print """\nIndico needs to store some information in the filesystem (database, cache, temporary files, logs...)
269 Please specify the directory where you'd like it to be placed.
270 (Note that putting it outside of your sourcecode tree is recommended)"""
271 prefixDirDefault = os.path.dirname(os.getcwd())
272 prefixDir = raw_input('[%s]: ' % prefixDirDefault).strip()
274 if prefixDir == '':
275 prefixDir = prefixDirDefault
277 directories = dict((d, os.path.join(prefixDir, d)) for d in
278 ['db', 'log', 'tmp', 'cache', 'archive'])
280 print 'Creating directories...',
281 for d in directories.values():
282 if not os.path.exists(d):
283 os.makedirs(d)
284 print 'Done!'
286 directories['htdocs'] = os.path.join(os.getcwd(), 'indico', 'htdocs')
287 directories['bin'] = os.path.join(os.getcwd(), 'bin')
288 directories['etc'] = os.path.join(os.getcwd(), 'etc')
289 directories['doc'] = os.path.join(os.getcwd(), 'doc')
291 self._update_conf_dir_paths(local, directories)
293 directories.pop('htdocs') #avoid modifying the htdocs folder permissions (it brings problems with git)
295 from MaKaC.consoleScripts.installBase import _databaseText, _findApacheUserGroup, _checkDirPermissions, _updateDbConfigFiles, _updateMaKaCEggCache
297 user = ''
299 sourcePath = os.getcwd()
301 if sys.platform == "linux2":
302 # find the apache user/group
303 user, group = _findApacheUserGroup(None, None)
304 _checkDirPermissions(directories, dbInstalledBySetupPy = directories['db'], accessuser = user, accessgroup = group)
306 _updateDbConfigFiles(directories['db'], directories['log'], os.path.join(sourcePath, 'etc'), directories['tmp'], user)
308 _updateMaKaCEggCache(os.path.join(os.path.dirname(__file__), 'indico', 'MaKaC', '__init__.py'), directories['tmp'])
310 updateIndicoConfPathInsideMaKaCConfig(os.path.join(os.path.dirname(__file__), ''), 'indico/MaKaC/common/MaKaCConfig.py')
311 compileAllLanguages(self)
312 print '''
314 ''' % _databaseText('etc')
316 if sys.platform == "linux2":
317 # create symlink to legacy MaKaC dir
318 # this is so that the ".egg-link" created by the "develop" command works
319 os.symlink('indico/MaKaC','MaKaC')
321 def _update_conf_dir_paths(self, filePath, dirs):
322 fdata = open(filePath).read()
323 for dir in dirs.items():
324 d = dir[1].replace("\\","/") # For Windows users
325 fdata = re.sub('\/opt\/indico\/%s'%dir[0], d, fdata)
326 open(filePath, 'w').write(fdata)
328 class test_indico(Command):
330 Test command for Indico
333 description = "Test Suite Framework"
334 user_options = [('specify=', None, "Use nosetests style (file.class:testcase)"),
335 ('coverage', None, "Output coverage report in html"),
336 ('unit', None, "Run only Unit tests"),
337 ('functional', None, "Run only Functional tests"),
338 ('pylint', None, "Run python source analysis"),
339 ('jsunit', None, "Run js unit tests"),
340 ('jslint', None, "Run js source analysis"),
341 ('jscoverage', None, "Output coverage report in html for js"),
342 ('jsspecify=', None, "Use js-test-driver style (TestCaseName.testName)"),
343 ('log=', None, "Log to console, using specified level"),
344 ('grid', None, "Use Selenium Grid"),
345 ('xml', None, "XML output"),
346 ('html', None, "Make an HTML report (when possible)"),
347 ('record', None, "Record tests (for --functional)"),
348 ('parallel', None, "Parallel test execution using Selenium Grid (for --functional)"),
349 ('threads=', None, "Parallel test execution with several threads (for --functional)"),
350 ('repeat=', None, "Number of repetitions (for --functional)"),
351 ('silent', None, "Don't output anything in the console, just generate the report")]
352 boolean_options = []
354 specify = None
355 coverage = False
356 unit = False
357 functional = False
358 pylint = False
359 jsunit = False
360 jslint = False
361 jscoverage = False
362 jsspecify = None
363 grid = None
364 silent = False
365 html = False
366 record = False
367 parallel = False
368 threads = False
369 repeat = False
370 log = False
371 xml = False
373 def initialize_options(self):
374 pass
376 def finalize_options(self):
377 pass
379 def run(self):
381 if not self.checkTestPackages():
382 print "Please install those missing packages before launching the tests again"
383 sys.exit(-1)
385 #missing jars will be downloaded automatically
386 if not self.checkTestJars():
387 print "Some jars could not be downloaded. Please download the missing jars manually"
388 sys.exit(-1)
390 from indico.tests import TestManager, TEST_RUNNERS
391 testsToRun = []
393 allTests = TEST_RUNNERS.keys()
395 for testType in allTests:
396 if getattr(self, testType):
397 testsToRun.append(testType)
399 if self.jsspecify and 'jsunit' not in testsToRun:
400 testsToRun.append('jsunit')
402 if testsToRun == []:
403 testsToRun = allTests
406 options = {'silent': self.silent,
407 'html': self.html,
408 'specify': self.specify,
409 'coverage': self.coverage,
410 'record': self.record,
411 'parallel': self.parallel,
412 'threads': self.threads,
413 'repeat': self.repeat,
414 'log': self.log,
415 'xml':self.xml}
417 # get only options that are active
418 options = dict((k,v) for (k,v) in options.iteritems() if v)
420 manager = TestManager()
422 result = manager.main(testsToRun, options)
424 sys.exit(result)
426 def checkTestPackages(self):
427 packagesList = ['figleaf',
428 'nose',
429 'rednose',
430 'selenium',
431 'twill']
432 validPackages = True
434 for package in packagesList:
435 try:
436 pkg_resources.require(package)
437 except pkg_resources.DistributionNotFound:
438 print """
439 %s not found! Please install it.
440 i.e. try 'easy_install %s'""" % (package, package)
441 validPackages = False
442 return validPackages
444 def checkTestJars(self):
446 check if needed jars are here, if not,
447 dowload them and unzip the file if necessary
450 from indico.tests import TestConfig
452 jarsList = {}
453 currentFilePath = os.path.dirname(__file__)
454 testModPath = os.path.join(currentFilePath, 'indico', 'tests')
456 try:
457 jarsList['jsunit'] = {'path': os.path.join(testModPath,
458 'javascript',
459 'unit'),
460 'url': TestConfig.getInstance().getJSUnitURL(),
461 'filename': TestConfig.getInstance().getJSUnitFilename()}
463 jarsList['jscoverage'] = {'path': os.path.join(testModPath,
464 'javascript',
465 'unit',
466 'plugins'),
467 'url': TestConfig.getInstance().getJSCoverageURL(),
468 'filename': TestConfig.getInstance().getJSCoverageFilename()}
470 jarsList['selenium'] = {'path': os.path.join(testModPath,
471 'python',
472 'functional'),
473 'url': TestConfig.getInstance().getSeleniumURL(),
474 'filename': TestConfig.getInstance().getSeleniumFilename()}
475 except KeyError, key:
476 print "[ERR] Please specify a value for %s in tests.conf" % key
477 sys.exit(1)
479 validJars = True
481 for name in jarsList:
482 jar = jarsList[name]
483 #check if jar is already here
484 if not os.path.exists(os.path.join(jar['path'], jar['filename'])):
485 print "Downloading %s to %s..." % (jar['url'], jar['path'])
486 try:
487 self.download(jar['url'], jar['path'])
488 except IOError, e:
489 validJars = validJars and False
490 print 'Could not download %s from %s (%s)' % (jar['filename'], jar['url'], e)
492 return validJars
494 def download(self, url, path):
495 """Copy the contents of a file from a given URL
496 to a local file.
498 import urllib
499 webFile = urllib.urlopen(url)
500 localFile = open(os.path.join(path, url.split('/')[-1]), 'w')
501 localFile.write(webFile.read())
502 webFile.close()
503 localFile.close()
505 def unzip(self, zipPath, inZipPath, targetFile):
506 """extract the needed file from zip and then delete the zip"""
507 import zipfile
508 try:
509 zfobj = zipfile.ZipFile(zipPath)
510 outfile = open(targetFile, 'wb')
511 outfile.write(zfobj.read(inZipPath))
512 outfile.flush()
513 outfile.close()
515 #delete zip file
516 os.unlink(zipPath)
517 except NameError, e:
518 print e
521 class egg_filename(Command):
522 description = "Get the file name of the generated egg"
523 user_options = []
524 boolean_options = []
526 def initialize_options(self):
527 pass
529 def finalize_options(self):
530 ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
531 self.egg_info = ei_cmd.egg_info
533 basename = pkg_resources.Distribution(
534 None, None, ei_cmd.egg_name, ei_cmd.egg_version,
535 get_python_version(),
536 self.distribution.has_ext_modules() and pkg_utils.get_build_platform
537 ).egg_name()
539 print basename
542 def run(self):
543 pass
546 if __name__ == '__main__':
547 # Always load source from the current folder
548 sys.path = [os.path.abspath('indico')] + sys.path
550 #PWD_INDICO_CONF = 'etc/indico.conf'
551 #if not os.path.exists(PWD_INDICO_CONF):
552 # shutil.copy('etc/indico.conf.sample', PWD_INDICO_CONF)
554 from MaKaC.consoleScripts.installBase import *
557 #Dirty trick: For running tests, we need to load all the modules and get rid of unnecessary outputs
558 tempLoggingDir = None
559 if 'test' in sys.argv:
560 import logging
561 import tempfile
562 tempLoggingDir = tempfile.mkdtemp()
563 logging.basicConfig(filename=os.path.join(tempLoggingDir, 'logging'),
564 level=logging.DEBUG)
565 setIndicoInstallMode(False)
566 else:
567 setIndicoInstallMode(True)
569 x = vars()
570 x.packageDir = os.path.join(get_python_lib(), 'MaKaC')
573 x.binDir = 'bin'
574 x.documentationDir = 'doc'
575 x.configurationDir = 'etc'
576 x.htdocsDir = 'htdocs'
578 dataFiles = _getDataFiles(x)
580 foundPackages = find_packages(where = 'indico',
581 exclude = ('htdocs*', 'tests*', 'core*', 'ext*',
582 'modules*', 'util*', 'web*', 'locale'))
584 # add our namespace package
585 foundPackages += list('indico.%s' % pkg for pkg in
586 find_packages(where = 'indico',
587 exclude = ('htdocs*','MaKaC*')))
589 foundPackages.append('indico')
591 cmdclass = {'sdist': sdist_indico,
592 'bdist': _bdist_indico(dataFiles),
593 'bdist_egg': _bdist_egg_indico(dataFiles),
594 'jsbuild': jsbuild,
595 'fetchdeps': fetchdeps_indico,
596 'develop_config': develop_indico,
597 'test': test_indico,
598 'egg_filename': egg_filename
601 if BABEL_PRESENT:
602 for cmdname in ['init_catalog', 'extract_messages', 'compile_catalog', 'update_catalog']:
603 cmdclass['%s_js' % cmdname] = getattr(babel, cmdname)
604 cmdclass['compile_catalog_js'] = i18n.generate_messages_js
606 setup(name = "indico",
607 cmdclass = cmdclass,
608 version = _versionInit(),
609 description = "Indico is a full-featured conference lifecycle management and meeting/lecture scheduling tool",
610 author = "Indico Team",
611 author_email = "indico-team@cern.ch",
612 url = "http://indico-software.org",
613 download_url = "http://indico-software.org/wiki/Releases/Indico0.97.0",
614 platforms = ["any"],
615 long_description = "Indico allows you to schedule conferences, from single talks to complex meetings with sessions and contributions. It also includes an advanced user delegation mechanism, allows paper reviewing, archival of conference information and electronic proceedings",
616 license = "http://www.gnu.org/licenses/gpl-2.0.txt",
617 package_dir = { 'indico': 'indico',
618 'MaKaC' : os.path.join('indico', 'MaKaC')},
619 entry_points = """
620 [console_scripts]
622 indico_scheduler = indico.modules.scheduler.daemon_script:main
623 indico_initial_setup = MaKaC.consoleScripts.indicoInitialSetup:main
624 indico_ctl = MaKaC.consoleScripts.indicoCtl:main
625 indico_livesync = indico.ext.livesync.console:main
626 indico_shell = indico.util.shell:main
628 [indico.ext_types]
630 Collaboration = MaKaC.plugins.Collaboration
631 InstantMessaging = MaKaC.plugins.InstantMessaging
632 RoomBooking = MaKaC.plugins.RoomBooking
633 EPayment = MaKaC.plugins.EPayment
634 livesync = indico.ext.livesync
635 importer = indico.ext.importer
638 [indico.ext]
640 Collaboration.EVO = MaKaC.plugins.Collaboration.EVO
641 Collaboration.Vidyo = MaKaC.plugins.Collaboration.Vidyo
642 Collaboration.CERNMCU = MaKaC.plugins.Collaboration.CERNMCU
643 Collaboration.RecordingManager = MaKaC.plugins.Collaboration.RecordingManager
644 Collaboration.RecordingRequest = MaKaC.plugins.Collaboration.RecordingRequest
645 Collaboration.WebcastRequest = MaKaC.plugins.Collaboration.WebcastRequest
647 RoomBooking.CERN = MaKaC.plugins.RoomBooking.CERN
648 RoomBooking.default = MaKaC.plugins.RoomBooking.default
650 EPayment.payPal = MaKaC.plugins.EPayment.payPal
651 EPayment.worldPay = MaKaC.plugins.EPayment.worldPay
652 EPayment.yellowPay = MaKaC.plugins.EPayment.yellowPay
653 EPayment.skipjack = MaKaC.plugins.EPayment.skipjack
655 importer.invenio = indico.ext.importer.invenio
656 importer.dummy = indico.ext.importer.dummy
658 InstantMessaging.XMPP = MaKaC.plugins.InstantMessaging.XMPP
660 livesync.invenio = indico.ext.livesync.invenio
661 livesync.cern_search = indico.ext.livesync.cern_search
663 """,
664 zip_safe = False,
665 packages = foundPackages,
666 namespace_packages = ['indico'],
667 install_requires = _getInstallRequires(),
668 data_files = dataFiles,
669 package_data = {'indico': ['*.*'] },
670 include_package_data = True,
671 dependency_links = [
672 EXTRA_RESOURCES_URL
676 #delete the temp folder used for logging
677 if 'test' in sys.argv:
678 shutil.rmtree(tempLoggingDir)