source: trunk/setup.py

Last change on this file was f2f7c1d, checked in by Itamar Turner-Trauring <itamar@…>, at 2024-01-26T14:24:59Z

Fix PyPy?.

  • Property mode set to 100644
File size: 17.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
14from io import open
15
16basedir = os.path.dirname(os.path.abspath(__file__))
17
18# locate our version number
19
20def read_version_py(infname):
21    try:
22        verstrline = open(infname, "rt").read()
23    except EnvironmentError:
24        return None
25    else:
26        VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
27        mo = re.search(VSRE, verstrline, re.M)
28        if mo:
29            return mo.group(1)
30
31VERSION_PY_FILENAME = 'src/allmydata/_version.py'
32version = read_version_py(VERSION_PY_FILENAME)
33
34install_requires = [
35    # importlib.resources.files and friends are new in Python 3.9.
36    "importlib_resources; python_version < '3.9'",
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.0",
42
43    # * foolscap < 0.5.1 had a performance bug which spent O(N**2) CPU for
44    #   transferring large mutable files of size N.
45    # * foolscap < 0.6 is incompatible with Twisted 10.2.0.
46    # * foolscap 0.6.1 quiets a DeprecationWarning.
47    # * foolscap < 0.6.3 is incompatible with Twisted 11.1.0 and newer.
48    # * foolscap 0.8.0 generates 2048-bit RSA-with-SHA-256 signatures,
49    #   rather than 1024-bit RSA-with-MD5. This also allows us to work
50    #   with a FIPS build of OpenSSL.
51    # * foolscap >= 0.12.3 provides tcp/tor/i2p connection handlers we need,
52    #   and allocate_tcp_port
53    # * foolscap >= 0.12.5 has ConnectionInfo and ReconnectionInfo
54    # * foolscap >= 0.12.6 has an i2p.sam_endpoint() that takes kwargs
55    # * foolscap 0.13.2 drops i2p support completely
56    # * foolscap >= 21.7 is necessary for Python 3 with i2p support.
57    # * foolscap >= 23.3 is necessary for Python 3.11.
58    "foolscap >= 21.7.0",
59    "foolscap >= 23.3.0; python_version > '3.10'",
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    # * Used for custom HTTPS validation
68    "pyOpenSSL >= 23.2.0",
69
70    # * The SFTP frontend depends on Twisted 11.0.0 to fix the SSH server
71    #   rekeying bug <https://twistedmatrix.com/trac/ticket/4395>
72    # * The SFTP frontend and manhole depend on the conch extra. However, we
73    #   can't explicitly declare that without an undesirable dependency on gmpy,
74    #   as explained in ticket #2740.
75    # * Due to a setuptools bug, we need to declare a dependency on the tls
76    #   extra even though we only depend on it via foolscap.
77    # * Twisted >= 15.1.0 is the first version that provided the [tls] extra.
78    # * Twisted-16.1.0 fixes https://twistedmatrix.com/trac/ticket/8223,
79    #   which otherwise causes test_system to fail (DirtyReactorError, due to
80    #   leftover timers)
81    # * Twisted-16.4.0 introduces `python -m twisted.trial` which is needed
82    #   for coverage testing
83    # * Twisted 16.6.0 drops the undesirable gmpy dependency from the conch
84    #   extra, letting us use that extra instead of trying to duplicate its
85    #   dependencies here.  Twisted[conch] >18.7 introduces a dependency on
86    #   bcrypt.  It is nice to avoid that if the user ends up with an older
87    #   version of Twisted.  That's hard to express except by using the extra.
88    #
89    # * Twisted 18.4.0 adds `client` and `host` attributes to `Request` in the
90    # * initializer, needed by logic in our custom `Request` subclass.
91    #
92    #   In a perfect world, Twisted[conch] would be a dependency of an "sftp"
93    #   extra.  However, pip fails to resolve the dependencies all
94    #   dependencies when asked for Twisted[tls] *and* Twisted[conch].
95    #   Specifically, "Twisted[conch]" (as the later requirement) is ignored.
96    #   If there were an Tahoe-LAFS sftp extra that dependended on
97    #   Twisted[conch] and install_requires only included Twisted[tls] then
98    #   `pip install tahoe-lafs[sftp]` would not install requirements
99    #   specified by Twisted[conch].  Since this would be the *whole point* of
100    #   an sftp extra in Tahoe-LAFS, there is no point in having one.
101    # * Twisted 19.10 introduces Site.getContentFile which we use to get
102    #   temporary upload files placed into a per-node temporary directory.
103    # * Twisted 22.8.0 added support for coroutine-returning functions in many
104    #   places (mainly via `maybeDeferred`)
105    "Twisted[tls,conch] >= 22.8.0",
106
107    "PyYAML >= 3.11",
108
109    "six >= 1.10.0",
110
111    # for 'tahoe invite' and 'tahoe join'
112    "magic-wormhole >= 0.10.2",
113
114    # We want a new enough version to support custom JSON encoders.
115    "eliot >= 1.14.0",
116
117    "pyrsistent",
118
119    # A great way to define types of values.
120    "attrs >= 20.1.0",
121
122    # WebSocket library for twisted and asyncio
123    "autobahn >= 22.4.3",
124
125    # Support for Python 3 transition
126    "future >= 0.18.2",
127
128    # Discover local network configuration
129    "netifaces",
130
131    # Utility code:
132    "pyutil >= 3.3.0",
133
134    # Linux distribution detection:
135    "distro >= 1.4.0",
136
137    # For the RangeMap datastructure. Need 2.0.2 at least for bugfixes.
138    "collections-extended >= 2.0.2",
139
140    # HTTP server and client
141    # Latest version is necessary to work with latest werkzeug:
142    "klein >= 23.5.0",
143    # 2.2.0 has a bug: https://github.com/pallets/werkzeug/issues/2465
144    "werkzeug != 2.2.0",
145    "treq",
146    # 5.6.0 excluded because https://github.com/agronholm/cbor2/issues/208
147    "cbor2 != 5.6.0",
148
149    # 0.6 adds the ability to decode CBOR. 0.6.1 fixes PyPy.
150    "pycddl >= 0.6.1",
151
152    # Command-line parsing
153    "click >= 8.1.1",
154
155    # for pid-file support
156    "psutil",
157    "filelock",
158]
159
160tor_requires = [
161    # 23.5 added support for custom TLS contexts in web_agent(), which is
162    # needed for the HTTP storage client to run over Tor.
163    "txtorcon >= 23.5.0",
164]
165
166i2p_requires = [
167    # txi2p has Python 3 support in master branch, but it has not been
168    # released -- see https://github.com/str4d/txi2p/issues/10.  We
169    # could use a fork for Python 3 until txi2p's maintainers are back
170    # in action.  For Python 2, we could continue using the txi2p
171    # version about which no one has complained to us so far.
172    "txi2p; python_version < '3.0'",
173    "txi2p-tahoe >= 0.3.5; python_version > '3.0'",
174]
175
176if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
177    del sys.argv[1]
178    install_requires += ["fakedependency >= 1.0.0"]
179
180from setuptools import find_packages, setup
181from setuptools import Command
182from setuptools.command import install
183
184
185trove_classifiers=[
186    "Development Status :: 5 - Production/Stable",
187    "Environment :: Console",
188    "Environment :: Web Environment",
189    "License :: OSI Approved :: GNU General Public License (GPL)",
190    "License :: DFSG approved",
191    "License :: Other/Proprietary License",
192    "Intended Audience :: Developers",
193    "Intended Audience :: End Users/Desktop",
194    "Intended Audience :: System Administrators",
195    "Operating System :: Microsoft",
196    "Operating System :: Microsoft :: Windows",
197    "Operating System :: Unix",
198    "Operating System :: POSIX :: Linux",
199    "Operating System :: POSIX",
200    "Operating System :: MacOS :: MacOS X",
201    "Operating System :: OS Independent",
202    "Natural Language :: English",
203    "Programming Language :: C",
204    "Programming Language :: Python",
205    "Programming Language :: Python :: 3",
206    "Topic :: Utilities",
207    "Topic :: System :: Systems Administration",
208    "Topic :: System :: Filesystems",
209    "Topic :: System :: Distributed Computing",
210    "Topic :: Software Development :: Libraries",
211    "Topic :: System :: Archiving :: Backup",
212    "Topic :: System :: Archiving :: Mirroring",
213    "Topic :: System :: Archiving",
214    ]
215
216
217GIT_VERSION_BODY = '''
218# This _version.py is generated from git metadata by the tahoe setup.py.
219
220__pkgname__ = "%(pkgname)s"
221real_version = "%(version)s"
222full_version = "%(full)s"
223branch = "%(branch)s"
224verstr = "%(normalized)s"
225__version__ = verstr
226'''
227
228def run_command(args, cwd=None):
229    use_shell = sys.platform == "win32"
230    try:
231        p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd, shell=use_shell)
232    except EnvironmentError as e:  # if this gives a SyntaxError, note that Tahoe-LAFS requires Python 3.8+
233        print("Warning: unable to run %r." % (" ".join(args),))
234        print(e)
235        return None
236    stdout = p.communicate()[0].strip()
237    if p.returncode != 0:
238        print("Warning: %r returned error code %r." % (" ".join(args), p.returncode))
239        return None
240    return stdout
241
242
243def versions_from_git(tag_prefix):
244    # This runs 'git' from the directory that contains this file. That either
245    # means someone ran a setup.py command (and this code is in
246    # versioneer.py, thus the containing directory is the root of the source
247    # tree), or someone ran a project-specific entry point (and this code is
248    # in _version.py, thus the containing directory is somewhere deeper in
249    # the source tree). This only gets called if the git-archive 'subst'
250    # variables were *not* expanded, and _version.py hasn't already been
251    # rewritten with a short version string, meaning we're inside a checked
252    # out source tree.
253
254    # versions_from_git (as copied from python-versioneer) returns strings
255    # like "1.9.0-25-gb73aba9-dirty", which means we're in a tree with
256    # uncommited changes (-dirty), the latest checkin is revision b73aba9,
257    # the most recent tag was 1.9.0, and b73aba9 has 25 commits that weren't
258    # in 1.9.0 . The narrow-minded NormalizedVersion parser that takes our
259    # output (meant to enable sorting of version strings) refuses most of
260    # that. Tahoe uses a function named suggest_normalized_version() that can
261    # handle "1.9.0.post25", so dumb down our output to match.
262
263    try:
264        source_dir = os.path.dirname(os.path.abspath(__file__))
265    except NameError as e:
266        # some py2exe/bbfreeze/non-CPython implementations don't do __file__
267        print("Warning: unable to find version because we could not obtain the source directory.")
268        print(e)
269        return {}
270    stdout = run_command(["git", "describe", "--tags", "--dirty", "--always"],
271                         cwd=source_dir)
272    if stdout is None:
273        # run_command already complained.
274        return {}
275    stdout = stdout.decode("ascii")
276    if not stdout.startswith(tag_prefix):
277        print("Warning: tag %r doesn't start with prefix %r." % (stdout, tag_prefix))
278        return {}
279    version = stdout[len(tag_prefix):]
280    pieces = version.split("-")
281    if len(pieces) == 1:
282        normalized_version = pieces[0]
283    else:
284        normalized_version = "%s.post%s" % (pieces[0], pieces[1])
285
286    stdout = run_command(["git", "rev-parse", "HEAD"], cwd=source_dir)
287    if stdout is None:
288        # run_command already complained.
289        return {}
290    full = stdout.decode("ascii").strip()
291    if version.endswith("-dirty"):
292        full += "-dirty"
293        normalized_version += ".dev0"
294
295    # Thanks to Jistanidiot at <http://stackoverflow.com/questions/6245570/get-current-branch-name>.
296    stdout = run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=source_dir)
297    branch = (stdout or b"unknown").decode("ascii").strip()
298
299    # this returns native strings (bytes on py2, unicode on py3)
300    return {"version": version, "normalized": normalized_version,
301            "full": full, "branch": branch}
302
303# setup.cfg has an [aliases] section which runs "update_version" before many
304# commands (like "build" and "sdist") that need to know our package version
305# ahead of time. If you add different commands (or if we forgot some), you
306# may need to add it to setup.cfg and configure it to run update_version
307# before your command.
308
309class UpdateVersion(Command):
310    description = "update _version.py from revision-control metadata"
311    user_options = install.install.user_options
312
313    def initialize_options(self):
314        pass
315    def finalize_options(self):
316        pass
317    def run(self):
318        global version
319        verstr = version
320        if os.path.isdir(os.path.join(basedir, ".git")):
321            verstr = self.try_from_git()
322
323        if verstr:
324            self.distribution.metadata.version = verstr
325        else:
326            print("""\
327********************************************************************
328Warning: no version information found. This may cause tests to fail.
329********************************************************************
330""")
331
332    def try_from_git(self):
333        # If we change the release tag names, we must change this too
334        versions = versions_from_git("tahoe-lafs-")
335
336        # setup.py might be run by either py2 or py3 (when run by tox, which
337        # uses py3 on modern debian/ubuntu distros). We want this generated
338        # file to contain native strings on both (str=bytes in py2,
339        # str=unicode in py3)
340        if versions:
341            body = GIT_VERSION_BODY % {
342                "pkgname": self.distribution.get_name(),
343                "version": versions["version"],
344                "normalized": versions["normalized"],
345                "full": versions["full"],
346                "branch": versions["branch"],
347                }
348            f = open(VERSION_PY_FILENAME, "wb")
349            f.write(body.encode("ascii"))
350            f.close()
351            print("Wrote normalized version %r into '%s'" % (versions["normalized"], VERSION_PY_FILENAME))
352
353        return versions.get("normalized", None)
354
355class PleaseUseTox(Command):
356    user_options = []
357    def initialize_options(self):
358        pass
359    def finalize_options(self):
360        pass
361
362    def run(self):
363        print("ERROR: Please use 'tox' to run the test suite.")
364        sys.exit(1)
365
366setup_args = {}
367if version:
368    setup_args["version"] = version
369
370setup(name="tahoe-lafs", # also set in __init__.py
371      description='secure, decentralized, fault-tolerant file store',
372      long_description=open('README.rst', 'r', encoding='utf-8').read(),
373      author='the Tahoe-LAFS project',
374      author_email='tahoe-dev@lists.tahoe-lafs.org',
375      url='https://tahoe-lafs.org/',
376      license='GNU GPL', # see README.rst -- there is an alternative licence
377      cmdclass={"update_version": UpdateVersion,
378                "test": PleaseUseTox,
379                },
380      package_dir = {'':'src'},
381      packages=find_packages('src') + ['allmydata.test.plugins'],
382      classifiers=trove_classifiers,
383      # We support Python 3.8 or later, 3.13 is untested for now
384      python_requires=">=3.8, <3.13",
385      install_requires=install_requires,
386      extras_require={
387          # Duplicate the Twisted pywin32 dependency here.  See
388          # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2392 for some
389          # discussion.
390          ':sys_platform=="win32"': ["pywin32 != 226"],
391          "build": [
392              "dulwich",
393              "gpg",
394          ],
395
396          # Here are the dependencies required to set up a reproducible test
397          # environment.  This could be for CI or local development.  These
398          # are *not* library dependencies of the test suite itself.  They are
399          # the tools we use to run the test suite at all.
400          "testenv": [
401              # Pin all of these versions for the same reason you ever want to
402              # pin anything: to prevent new releases with regressions from
403              # introducing spurious failures into CI runs for whatever
404              # development work is happening at the time.  The versions
405              # selected here are just the current versions at the time.
406              # Bumping them to keep up with future releases is fine as long
407              # as those releases are known to actually work.
408              "pip==23.3.1",
409              "wheel==0.41.3",
410              "subunitreporter==23.8.0",
411              "python-subunit==1.4.2",
412              "junitxml==0.7",
413              "coverage==7.2.5",
414          ],
415
416          # Here are the library dependencies of the test suite.
417          "test": [
418              "mock",
419              "pytest",
420              "pytest-twisted",
421              "hypothesis >= 3.6.1",
422              "towncrier",
423              "testtools",
424              "fixtures",
425              "beautifulsoup4",
426              "html5lib",
427              # Pin old version until
428              # https://github.com/paramiko/paramiko/issues/1961 is fixed.
429              "paramiko < 2.9",
430              "pytest-timeout",
431              # Does our OpenMetrics endpoint adhere to the spec:
432              "prometheus-client == 0.11.0",
433          ] + tor_requires + i2p_requires,
434          "tor": tor_requires,
435          "i2p": i2p_requires,
436      },
437      package_data={"allmydata.web": ["*.xhtml",
438                                      "static/*.js", "static/*.png", "static/*.css",
439                                      "static/img/*.png",
440                                      "static/css/*.css",
441                                      ],
442                    "allmydata": ["ported-modules.txt"],
443                    },
444      include_package_data=True,
445      entry_points={
446          'console_scripts': [
447              'tahoe = allmydata.scripts.runner:run',
448              'grid-manager = allmydata.cli.grid_manager:grid_manager',
449          ]
450      },
451      **setup_args
452      )
Note: See TracBrowser for help on using the repository browser.