2 #_____________________________________________________________________________
4 # This file is part of BridgeDB, a Tor bridge distribution system.
6 # :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@torproject.org>
7 # Aaron Gibson 0x2C4B239DD876C9F6 <aagbsn@torproject.org>
8 # Nick Mathewson 0x21194EBB165733EA <nickm@torproject.org>
9 # please also see AUTHORS file
10 # :copyright: (c) 2007-2013, The Tor Project, Inc.
11 # (c) 2007-2013, all entities within the AUTHORS file
12 # :license: see LICENSE for licensing information
13 #_____________________________________________________________________________
15 from __future__
import print_function
23 # Fix circular dependency with setup.py install
25 from babel
.messages
.frontend
import compile_catalog
, extract_messages
26 from babel
.messages
.frontend
import init_catalog
, update_catalog
28 compile_catalog
= extract_messages
= init_catalog
= update_catalog
= None
35 # Repo directory that contains translations; this directory should contain
36 # both uncompiled translations (.po files) as well as compiled ones (.mo
37 # files). We only want to install the .mo files.
38 repo_i18n
= os
.path
.join(pkgpath
, 'i18n')
40 # The list of country codes for supported languages will be stored as a list
41 # variable, ``_supported``, in this file, so that the bridgedb packages
42 # __init__.py can access it:
43 repo_langs
= os
.path
.join(pkgpath
, '_langs.py')
45 # The directory containing template files and other resources to serve on the
47 repo_templates
= os
.path
.join(pkgpath
, 'distributors', 'https', 'templates')
49 # The directories to install non-sourcecode resources into should always be
50 # given as relative paths, in order to force distutils to install relative to
51 # the rest of the codebase.
53 # Directory to installed compiled translations (.mo files) into:
54 install_i18n
= os
.path
.join('bridgedb', 'i18n')
56 # Directory to install docs, license, and other text resources into:
57 install_docs
= os
.path
.join('share', 'doc', 'bridgedb')
61 """Get our cmdclass dictionary for use in setuptool.setup().
63 This must be done outside the call to setuptools.setup() because we need
64 to add our own classes to the cmdclass dictionary, and then update that
65 dictionary with the one returned from versioneer.get_cmdclass().
67 cmdclass
= {'test': Trial
,
68 'compile_catalog': compile_catalog
,
69 'extract_messages': extract_messages
,
70 'init_catalog': init_catalog
,
71 'update_catalog': update_catalog
}
72 cmdclass
.update(versioneer
.get_cmdclass())
75 def get_requirements():
76 """Extract the list of requirements from our requirements.txt.
79 :returns: Two lists, the first is a list of requirements in the form of
80 pkgname==version. The second is a list of URIs or VCS checkout strings
81 which specify the dependency links for obtaining a copy of the
84 requirements_file
= os
.path
.join(os
.getcwd(), 'requirements.txt')
88 with
open(requirements_file
) as reqfile
:
89 for line
in reqfile
.readlines():
91 if line
.startswith('#'):
93 if line
.startswith(('git+', 'hg+', 'svn+')):
94 line
= line
[line
.index('+') + 1:]
96 ('https://', 'git://', 'hg://', 'svn://')):
99 requirements
.append(line
)
101 except (IOError, OSError) as error
:
104 return requirements
, links
106 def get_supported_langs():
107 """Get the paths for all compiled translation files.
109 The two-letter country code of each language which is going to be
110 installed will be added to a list, and this list will be written to
111 :attr:`repo_langs`, so that bridgedb/__init__.py can store a
112 package-level attribute ``bridgedb.__langs__``, which will be a list of
113 any languages which were installed.
115 Then, the paths of the compiled translations files are added to
116 :ivar:`data_files`. These should be included in the ``data_files``
117 parameter in :func:`~setuptools.setup` in order for setuptools to be able
118 to tell the underlying distutils ``install_data`` command to include these
121 See http://docs.python.org/2/distutils/setupscript.html#installing-additional-files
122 for more information.
124 :ivar list supported: A list of two-letter country codes, one for each
125 language we currently provide translations support for.
126 :ivar list lang_dirs: The directories (relative or absolute) to install
127 the compiled translation file to.
128 :ivar list lang_files: The paths to compiled translations files, relative
129 to this setup.py script.
131 :returns: Two lists, ``lang_dirs`` and ``lang_files``.
137 for lang
in os
.listdir(repo_i18n
):
138 if lang
.endswith('templates'):
140 supported
.append(lang
)
141 lang_dirs
.append(os
.path
.join(install_i18n
, lang
))
142 lang_files
.append(os
.path
.join(repo_i18n
, lang
,
143 'LC_MESSAGES', 'bridgedb.mo'))
146 # Write our list of supported languages to 'bridgedb/_langs.py':
148 with
open(repo_langs
, 'r') as langsfile
:
149 for line
in langsfile
.readlines():
150 if line
.startswith('supported'):
151 # Change the 'supported' list() into a set():
152 line
= "supported = set(%s)\n" % supported
153 new_langs_lines
.append(line
)
154 with
open(repo_langs
, 'w') as newlangsfile
:
155 for line
in new_langs_lines
:
156 newlangsfile
.write(line
)
158 return lang_dirs
, lang_files
160 def get_template_files():
161 """Return the paths to any web resource files to include in the package.
164 :returns: Any files in :attr:`repo_templates` which match one of the glob
165 patterns in :ivar:`include_patterns`.
167 include_patterns
= ['*.html',
173 'assets/font/*.woff',
178 'assets/images/*.svg']
181 for include_pattern
in include_patterns
:
182 pattern
= os
.path
.join(repo_templates
, include_pattern
)
183 matches
= glob(pattern
)
184 template_files
.extend(matches
)
186 return template_files
188 def get_data_files(filesonly
=False):
189 """Return any hard-coded data_files which should be distributed.
191 This is necessary so that both the distutils-derived :class:`installData`
192 class and the setuptools ``data_files`` parameter include the same files.
193 Call this function with ``filesonly=True`` to get a list of files suitable
194 for giving to the ``package_data`` parameter in ``setuptools.setup()``.
195 Or, call it with ``filesonly=False`` (the default) to get a list which is
196 suitable for using as ``distutils.command.install_data.data_files``.
198 :param bool filesonly: If true, only return the locations of the files to
199 install, not the directories to install them into.
201 :returns: If ``filesonly``, returns a list of file paths. Otherwise,
202 returns a list of 2-tuples containing: one, the directory to install
203 to, and two, the files to install to that directory.
206 doc_files
= ['README', 'TODO', 'LICENSE', 'requirements.txt']
207 lang_dirs
, lang_files
= get_supported_langs()
208 template_files
= get_template_files()
211 data_files
.extend(doc_files
)
212 for lst
in lang_files
, template_files
:
214 if filename
.startswith(pkgpath
):
215 # The +1 gets rid of the '/' at the beginning:
216 filename
= filename
[len(pkgpath
) + 1:]
217 data_files
.append(filename
)
219 data_files
.append((install_docs
, doc_files
))
220 for ldir
, lfile
in zip(lang_dirs
, lang_files
):
221 data_files
.append((ldir
, [lfile
,]))
223 #[sys.stdout.write("Added data_file '%s'\n" % x) for x in data_files]
228 class Trial(setuptools
.Command
):
229 """Twisted Trial setuptools command.
231 Based on the setuptools Trial command in Zooko's Tahoe-LAFS, as well as
232 https://github.com/simplegeo/setuptools-trial/ (which is also based on the
235 Pieces of the original implementation of this 'test' command (that is, for
236 the original pyunit-based BridgeDB tests which, a long time ago, in a
237 galaxy far far away, lived in bridgedb.Tests) were based on setup.py from
238 Nick Mathewson's mixminion, which was based on the setup.py from Zooko's
239 pyutil package, which was in turn based on
240 http://mail.python.org/pipermail/distutils-sig/2002-January/002714.html.
242 Crusty, old-ass Python, like hella wut.
244 description
= "Run Twisted Trial-based tests."
246 ('debug', 'b', ("Run tests in a debugger. If that debugger is pdb, will "
247 "load '.pdbrc' from current directory if it exists.")),
248 ('debug-stacktraces', 'B', "Report Deferred creation and callback stack traces"),
249 ('debugger=', None, ("The fully qualified name of a debugger to use if "
250 "--debug is passed (default: pdb)")),
251 ('disablegc', None, "Disable the garbage collector"),
252 ('force-gc', None, "Have Trial run gc.collect() before and after each test case"),
253 ('jobs=', 'j', "Number of local workers to run, a strictly positive integer"),
254 ('profile', None, "Run tests under the Python profiler"),
255 ('random=', 'Z', "Run tests in random order using the specified seed"),
256 ('reactor=', 'r', "Which reactor to use"),
257 ('reporter=', None, "Customize Trial's output with a reporter plugin"),
258 ('rterrors', 'e', "Realtime errors: print out tracebacks as soon as they occur"),
259 ('spew', None, "Print an insanely verbose log of everything that happens"),
260 ('testmodule=', None, "Filename to grep for test cases (-*- test-case-name)"),
261 ('tbformat=', None, ("Specify the format to display tracebacks with. Valid "
262 "formats are 'plain', 'emacs', and 'cgitb' which uses "
263 "the nicely verbose stdlib cgitb.text function")),
264 ('unclean-warnings', None, "Turn dirty reactor errors into warnings"),
265 ('until-failure', 'u', "Repeat a test (specified by -s) until it fails."),
266 ('without-module=', None, ("Fake the lack of the specified modules, separated "
269 boolean_options
= ['debug', 'debug-stacktraces', 'disablegc', 'force-gc',
270 'profile', 'rterrors', 'spew', 'unclean-warnings',
273 def initialize_options(self
):
275 self
.debug_stacktraces
= None
277 self
.disablegc
= None
286 self
.testmodule
= None
288 self
.unclean_warnings
= None
289 self
.until_failure
= None
290 self
.without_module
= None
292 def finalize_options(self
):
293 build
= self
.get_finalized_command('build')
294 self
.build_purelib
= build
.build_purelib
295 self
.build_platlib
= build
.build_platlib
298 self
.run_command('build')
299 old_path
= sys
.path
[:]
300 sys
.path
[0:0] = [self
.build_purelib
, self
.build_platlib
]
304 result
= self
.run_tests()
307 raise SystemExit(result
)
310 # We do the import from Twisted inside the function instead of the top
311 # of the file because since Twisted is a setup_requires, we can't
312 # assume that Twisted will be installed on the user's system prior, so
313 # if we don't do the import here, then importing from this plugin will
315 from twisted
.scripts
import trial
317 if not self
.testmodule
:
318 self
.testmodule
= "bridgedb.test"
320 # Handle parsing the trial options passed through the setuptools
323 for opt
in self
.boolean_options
:
324 if getattr(self
, opt
.replace('-', '_'), None):
325 cmd_options
.append('--%s' % opt
)
327 for opt
in ('debugger', 'jobs', 'random', 'reactor', 'reporter',
328 'testmodule', 'tbformat', 'without-module'):
329 value
= getattr(self
, opt
.replace('-', '_'), None)
330 if value
is not None:
331 cmd_options
.extend(['--%s' % opt
, value
])
333 config
= trial
.Options()
334 config
.parseOptions(cmd_options
)
335 config
['tests'] = [self
.testmodule
,]
337 trial
._initialDebugSetup
(config
)
338 trialRunner
= trial
._makeRunner
(config
)
339 suite
= trial
._getSuite
(config
)
342 if self
.until_failure
:
343 test_result
= trialRunner
.runUntilFailure(suite
)
345 test_result
= trialRunner
.run(suite
)
347 if test_result
.wasSuccessful():
352 # If there is an environment variable BRIDGEDB_INSTALL_DEPENDENCIES=0, it will
353 # disable checking for, fetching, and installing BridgeDB's dependencies with
356 # Setting BRIDGEDB_INSTALL_DEPENDENCIES=0 is *highly* recommended, because
357 # easy_install is a security nightmare. Automatically installing dependencies
358 # is enabled by default, however, because this is how all Python packages are
360 if bool(int(os
.environ
.get("BRIDGEDB_INSTALL_DEPENDENCIES", 1))):
361 requires
, deplinks
= get_requirements()
363 requires
, deplinks
= [], []
368 version
=versioneer
.get_version(),
369 description
='Backend systems for distribution of Tor bridge relays',
370 author
='Nick Mathewson',
371 author_email
='nickm at torproject dot org',
372 maintainer
='Philipp Winter',
373 maintainer_email
='phw@torproject.org',
374 url
='https://www.torproject.org',
375 download_url
='https://gitweb.torproject.org/bridgedb.git',
376 package_dir
={'bridgedb': 'bridgedb'},
377 packages
=['bridgedb',
378 'bridgedb.distributors',
379 'bridgedb.distributors.common',
380 'bridgedb.distributors.email',
381 'bridgedb.distributors.https',
382 'bridgedb.distributors.moat',
386 scripts
=['scripts/bridgedb',
387 'scripts/get-tor-exits'],
388 extras_require
={'test': ["sure==1.2.2",
390 "cryptography==1.9"]},
392 cmdclass
=get_cmdclass(),
393 include_package_data
=True,
394 install_requires
=requires
,
395 dependency_links
=deplinks
,
396 package_data
={'bridgedb': get_data_files(filesonly
=True)},
397 exclude_package_data
={'bridgedb': ['*.po', '*.pot']},
400 ('**.py', 'python', None),
401 ('distributors/https/templates/**.html', 'mako', None),