source: trunk/setup.py

Last change on this file was d74d7e7, checked in by Jason R. Coombs <jaraco@…>, at 2020-09-10T01:42:32Z

Merge branch 'master' into 3394.py38-tests

  • Property mode set to 100644
File size: 16.2 KB
Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3import sys
4
5# Tahoe-LAFS -- secure, distributed storage grid
6#
7# Copyright © 2006-2012 The Tahoe-LAFS Software Foundation
8#
9# This file is part of Tahoe-LAFS.
10#
11# See the docs/about.rst file for licensing information.
12
13import os, subprocess, re
14
15basedir = os.path.dirname(os.path.abspath(__file__))
16
17# locate our version number
18
19def read_version_py(infname):
20    try:
21        verstrline = open(infname, "rt").read()
22    except EnvironmentError:
23        return None
24    else:
25        VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
26        mo = re.search(VSRE, verstrline, re.M)
27        if mo:
28            return mo.group(1)
29
30VERSION_PY_FILENAME = 'src/allmydata/_version.py'
31version = read_version_py(VERSION_PY_FILENAME)
32
33install_requires = [
34    # we don't need much out of setuptools but the version checking stuff
35    # needs pkg_resources and PEP 440 version specifiers.
36    "setuptools >= 28.8.0",
37
38    "zfec >= 1.1.0",
39
40    # zope.interface >= 3.6.0 is required for Twisted >= 12.1.0.
41    # zope.interface 3.6.3 and 3.6.4 are incompatible with Nevow (#1435).
42    "zope.interface >= 3.6.0, != 3.6.3, != 3.6.4",
43
44    # * foolscap < 0.5.1 had a performance bug which spent O(N**2) CPU for
45    #   transferring large mutable files of size N.
46    # * foolscap < 0.6 is incompatible with Twisted 10.2.0.
47    # * foolscap 0.6.1 quiets a DeprecationWarning.
48    # * foolscap < 0.6.3 is incompatible with Twisted 11.1.0 and newer.
49    # * foolscap 0.8.0 generates 2048-bit RSA-with-SHA-256 signatures,
50    #   rather than 1024-bit RSA-with-MD5. This also allows us to work
51    #   with a FIPS build of OpenSSL.
52    # * foolscap >= 0.12.3 provides tcp/tor/i2p connection handlers we need,
53    #   and allocate_tcp_port
54    # * foolscap >= 0.12.5 has ConnectionInfo and ReconnectionInfo
55    # * foolscap >= 0.12.6 has an i2p.sam_endpoint() that takes kwargs
56    # * foolscap 0.13.2 drops i2p support completely
57    # * foolscap >= 20.4 is necessary for Python 3
58    "foolscap == 0.13.1 ; python_version < '3.0'",
59    "foolscap >= 20.4.0 ; python_version > '3.0'",
60
61    # * cryptography 2.6 introduced some ed25519 APIs we rely on.  Note that
62    #   Twisted[conch] also depends on cryptography and Twisted[tls]
63    #   transitively depends on cryptography.  So it's anyone's guess what
64    #   version of cryptography will *really* be installed.
65    "cryptography >= 2.6",
66
67    # * We need Twisted 10.1.0 for the FTP frontend in order for
68    #   Twisted's FTP server to support asynchronous close.
69    # * The SFTP frontend depends on Twisted 11.0.0 to fix the SSH server
70    #   rekeying bug <https://twistedmatrix.com/trac/ticket/4395>
71    # * The FTP frontend depends on Twisted >= 11.1.0 for
72    #   filepath.Permissions
73    # * Nevow 0.11.1 depends on Twisted >= 13.0.0.
74    # * The SFTP frontend and manhole depend on the conch extra. However, we
75    #   can't explicitly declare that without an undesirable dependency on gmpy,
76    #   as explained in ticket #2740.
77    # * Due to a setuptools bug, we need to declare a dependency on the tls
78    #   extra even though we only depend on it via foolscap.
79    # * Twisted >= 15.1.0 is the first version that provided the [tls] extra.
80    # * Twisted-16.1.0 fixes https://twistedmatrix.com/trac/ticket/8223,
81    #   which otherwise causes test_system to fail (DirtyReactorError, due to
82    #   leftover timers)
83    # * Twisted-16.4.0 introduces `python -m twisted.trial` which is needed
84    #   for coverage testing
85    # * Twisted 16.6.0 drops the undesirable gmpy dependency from the conch
86    #   extra, letting us use that extra instead of trying to duplicate its
87    #   dependencies here.  Twisted[conch] >18.7 introduces a dependency on
88    #   bcrypt.  It is nice to avoid that if the user ends up with an older
89    #   version of Twisted.  That's hard to express except by using the extra.
90    #
91    # * Twisted 18.4.0 adds `client` and `host` attributes to `Request` in the
92    # * initializer, needed by logic in our custom `Request` subclass.
93    #
94    #   In a perfect world, Twisted[conch] would be a dependency of an "sftp"
95    #   extra.  However, pip fails to resolve the dependencies all
96    #   dependencies when asked for Twisted[tls] *and* Twisted[conch].
97    #   Specifically, "Twisted[conch]" (as the later requirement) is ignored.
98    #   If there were an Tahoe-LAFS sftp extra that dependended on
99    #   Twisted[conch] and install_requires only included Twisted[tls] then
100    #   `pip install tahoe-lafs[sftp]` would not install requirements
101    #   specified by Twisted[conch].  Since this would be the *whole point* of
102    #   an sftp extra in Tahoe-LAFS, there is no point in having one.
103    "Twisted[tls,conch] >= 18.4.0",
104
105    # We need Nevow >= 0.11.1 which can be installed using pip.
106    "Nevow >= 0.11.1",
107
108    "PyYAML >= 3.11",
109
110    "six >= 1.10.0",
111
112    # for 'tahoe invite' and 'tahoe join'
113    "magic-wormhole >= 0.10.2",
114
115    # Eliot is contemplating dropping Python 2 support.  Stick to a version we
116    # know works on Python 2.7.
117    "eliot ~= 1.7",
118
119    # Pyrsistent 0.17.0 (which we use by way of Eliot) has dropped
120    # Python 2 entirely; stick to the version known to work for us.
121    # XXX: drop this bound: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3404
122    "pyrsistent < 0.17.0",
123
124    # A great way to define types of values.
125    # XXX: drop the upper bound: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3390
126    "attrs >= 18.2.0, < 20",
127
128    # WebSocket library for twisted and asyncio
129    "autobahn >= 19.5.2",
130
131    # Support for Python 3 transition
132    "future >= 0.18.2",
133
134    # Utility code:
135    "pyutil >= 3.3.0",
136
137    # Linux distribution detection:
138    "distro >= 1.4.0",
139]
140
141setup_requires = [
142    'setuptools >= 28.8.0',  # for PEP-440 style versions
143]
144
145tor_requires = [
146    # This is exactly what `foolscap[tor]` means but pip resolves the pair of
147    # dependencies "foolscap[i2p] foolscap[tor]" to "foolscap[i2p]" so we lose
148    # this if we don't declare it ourselves!
149    "txtorcon >= 0.17.0",
150]
151
152i2p_requires = [
153    # txi2p has Python 3 support, but it's unreleased: https://github.com/str4d/txi2p/issues/10.
154    # URL lookups are in PEP-508 (via https://stackoverflow.com/a/54794506).
155    # Also see the comment in tor_requires.
156    "txi2p @ git+https://github.com/str4d/txi2p@0611b9a86172cb70d2f5e415a88eee9f230590b3#egg=txi2p",
157]
158
159if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
160    del sys.argv[1]
161    install_requires += ["fakedependency >= 1.0.0"]
162
163from setuptools import find_packages, setup
164from setuptools import Command
165from setuptools.command import install
166
167
168trove_classifiers=[
169    "Development Status :: 5 - Production/Stable",
170    "Environment :: Console",
171    "Environment :: Web Environment",
172    "License :: OSI Approved :: GNU General Public License (GPL)",
173    "License :: DFSG approved",
174    "License :: Other/Proprietary License",
175    "Intended Audience :: Developers",
176    "Intended Audience :: End Users/Desktop",
177    "Intended Audience :: System Administrators",
178    "Operating System :: Microsoft",
179    "Operating System :: Microsoft :: Windows",
180    "Operating System :: Unix",
181    "Operating System :: POSIX :: Linux",
182    "Operating System :: POSIX",
183    "Operating System :: MacOS :: MacOS X",
184    "Operating System :: OS Independent",
185    "Natural Language :: English",
186    "Programming Language :: C",
187    "Programming Language :: Python",
188    "Programming Language :: Python :: 2",
189    "Programming Language :: Python :: 2.7",
190    "Topic :: Utilities",
191    "Topic :: System :: Systems Administration",
192    "Topic :: System :: Filesystems",
193    "Topic :: System :: Distributed Computing",
194    "Topic :: Software Development :: Libraries",
195    "Topic :: System :: Archiving :: Backup",
196    "Topic :: System :: Archiving :: Mirroring",
197    "Topic :: System :: Archiving",
198    ]
199
200
201GIT_VERSION_BODY = '''
202# This _version.py is generated from git metadata by the tahoe setup.py.
203
204__pkgname__ = "%(pkgname)s"
205real_version = "%(version)s"
206full_version = "%(full)s"
207branch = "%(branch)s"
208verstr = "%(normalized)s"
209__version__ = verstr
210'''
211
212def run_command(args, cwd=None):
213    use_shell = sys.platform == "win32"
214    try:
215        p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd, shell=use_shell)
216    except EnvironmentError as e:  # if this gives a SyntaxError, note that Tahoe-LAFS requires Python 2.7+
217        print("Warning: unable to run %r." % (" ".join(args),))
218        print(e)
219        return None
220    stdout = p.communicate()[0].strip()
221    if p.returncode != 0:
222        print("Warning: %r returned error code %r." % (" ".join(args), p.returncode))
223        return None
224    return stdout
225
226
227def versions_from_git(tag_prefix):
228    # This runs 'git' from the directory that contains this file. That either
229    # means someone ran a setup.py command (and this code is in
230    # versioneer.py, thus the containing directory is the root of the source
231    # tree), or someone ran a project-specific entry point (and this code is
232    # in _version.py, thus the containing directory is somewhere deeper in
233    # the source tree). This only gets called if the git-archive 'subst'
234    # variables were *not* expanded, and _version.py hasn't already been
235    # rewritten with a short version string, meaning we're inside a checked
236    # out source tree.
237
238    # versions_from_git (as copied from python-versioneer) returns strings
239    # like "1.9.0-25-gb73aba9-dirty", which means we're in a tree with
240    # uncommited changes (-dirty), the latest checkin is revision b73aba9,
241    # the most recent tag was 1.9.0, and b73aba9 has 25 commits that weren't
242    # in 1.9.0 . The narrow-minded NormalizedVersion parser that takes our
243    # output (meant to enable sorting of version strings) refuses most of
244    # that. Tahoe uses a function named suggest_normalized_version() that can
245    # handle "1.9.0.post25", so dumb down our output to match.
246
247    try:
248        source_dir = os.path.dirname(os.path.abspath(__file__))
249    except NameError as e:
250        # some py2exe/bbfreeze/non-CPython implementations don't do __file__
251        print("Warning: unable to find version because we could not obtain the source directory.")
252        print(e)
253        return {}
254    stdout = run_command(["git", "describe", "--tags", "--dirty", "--always"],
255                         cwd=source_dir)
256    if stdout is None:
257        # run_command already complained.
258        return {}
259    stdout = stdout.decode("ascii")
260    if not stdout.startswith(tag_prefix):
261        print("Warning: tag %r doesn't start with prefix %r." % (stdout, tag_prefix))
262        return {}
263    version = stdout[len(tag_prefix):]
264    pieces = version.split("-")
265    if len(pieces) == 1:
266        normalized_version = pieces[0]
267    else:
268        normalized_version = "%s.post%s" % (pieces[0], pieces[1])
269
270    stdout = run_command(["git", "rev-parse", "HEAD"], cwd=source_dir)
271    if stdout is None:
272        # run_command already complained.
273        return {}
274    full = stdout.decode("ascii").strip()
275    if version.endswith("-dirty"):
276        full += "-dirty"
277        normalized_version += ".dev0"
278
279    # Thanks to Jistanidiot at <http://stackoverflow.com/questions/6245570/get-current-branch-name>.
280    stdout = run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=source_dir)
281    branch = (stdout or b"unknown").decode("ascii").strip()
282
283    # this returns native strings (bytes on py2, unicode on py3)
284    return {"version": version, "normalized": normalized_version,
285            "full": full, "branch": branch}
286
287# setup.cfg has an [aliases] section which runs "update_version" before many
288# commands (like "build" and "sdist") that need to know our package version
289# ahead of time. If you add different commands (or if we forgot some), you
290# may need to add it to setup.cfg and configure it to run update_version
291# before your command.
292
293class UpdateVersion(Command):
294    description = "update _version.py from revision-control metadata"
295    user_options = install.install.user_options
296
297    def initialize_options(self):
298        pass
299    def finalize_options(self):
300        pass
301    def run(self):
302        global version
303        verstr = version
304        if os.path.isdir(os.path.join(basedir, ".git")):
305            verstr = self.try_from_git()
306
307        if verstr:
308            self.distribution.metadata.version = verstr
309        else:
310            print("""\
311********************************************************************
312Warning: no version information found. This may cause tests to fail.
313********************************************************************
314""")
315
316    def try_from_git(self):
317        # If we change the release tag names, we must change this too
318        versions = versions_from_git("tahoe-lafs-")
319
320        # setup.py might be run by either py2 or py3 (when run by tox, which
321        # uses py3 on modern debian/ubuntu distros). We want this generated
322        # file to contain native strings on both (str=bytes in py2,
323        # str=unicode in py3)
324        if versions:
325            body = GIT_VERSION_BODY % {
326                "pkgname": self.distribution.get_name(),
327                "version": versions["version"],
328                "normalized": versions["normalized"],
329                "full": versions["full"],
330                "branch": versions["branch"],
331                }
332            f = open(VERSION_PY_FILENAME, "wb")
333            f.write(body.encode("ascii"))
334            f.close()
335            print("Wrote normalized version %r into '%s'" % (versions["normalized"], VERSION_PY_FILENAME))
336
337        return versions.get("normalized", None)
338
339class PleaseUseTox(Command):
340    user_options = []
341    def initialize_options(self):
342        pass
343    def finalize_options(self):
344        pass
345
346    def run(self):
347        print("ERROR: Please use 'tox' to run the test suite.")
348        sys.exit(1)
349
350setup_args = {}
351if version:
352    setup_args["version"] = version
353
354setup(name="tahoe-lafs", # also set in __init__.py
355      description='secure, decentralized, fault-tolerant file store',
356      long_description=open('README.rst', 'rU').read(),
357      author='the Tahoe-LAFS project',
358      author_email='tahoe-dev@tahoe-lafs.org',
359      url='https://tahoe-lafs.org/',
360      license='GNU GPL', # see README.rst -- there is an alternative licence
361      cmdclass={"update_version": UpdateVersion,
362                "test": PleaseUseTox,
363                },
364      package_dir = {'':'src'},
365      packages=find_packages('src') + ['allmydata.test.plugins'],
366      classifiers=trove_classifiers,
367      # We support Python 2.7, and we're working on support for 3.6 (the
368      # highest version that PyPy currently supports).
369      python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*",
370      install_requires=install_requires,
371      extras_require={
372          # Duplicate the Twisted pywin32 dependency here.  See
373          # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2392 for some
374          # discussion.
375          ':sys_platform=="win32"': ["pywin32 != 226"],
376          "test": [
377              "flake8",
378              # Pin a specific pyflakes so we don't have different folks
379              # disagreeing on what is or is not a lint issue.  We can bump
380              # this version from time to time, but we will do it
381              # intentionally.
382              "pyflakes == 2.2.0",
383              # coverage 5.0 breaks the integration tests in some opaque way.
384              # This probably needs to be addressed in a more permanent way
385              # eventually...
386              "coverage ~= 4.5",
387              "mock",
388              "tox",
389              "pytest",
390              "pytest-twisted",
391              "hypothesis >= 3.6.1",
392              "treq",
393              "towncrier",
394              "testtools",
395              "fixtures",
396              "beautifulsoup4",
397              "html5lib",
398              "junitxml",
399              "tenacity",
400          ] + tor_requires + i2p_requires,
401          "tor": tor_requires,
402          "i2p": i2p_requires,
403      },
404      package_data={"allmydata.web": ["*.xhtml",
405                                      "static/*.js", "static/*.png", "static/*.css",
406                                      "static/img/*.png",
407                                      "static/css/*.css",
408                                      ],
409                    "allmydata": ["ported-modules.txt"],
410                    },
411      include_package_data=True,
412      setup_requires=setup_requires,
413      entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
414      **setup_args
415      )
Note: See TracBrowser for help on using the repository browser.