1 | #! /usr/bin/env python |
---|
2 | # -*- coding: utf-8 -*- |
---|
3 | import 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 | |
---|
13 | import os, subprocess, re |
---|
14 | from io import open |
---|
15 | |
---|
16 | basedir = os.path.dirname(os.path.abspath(__file__)) |
---|
17 | |
---|
18 | # locate our version number |
---|
19 | |
---|
20 | def 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 | |
---|
31 | VERSION_PY_FILENAME = 'src/allmydata/_version.py' |
---|
32 | version = read_version_py(VERSION_PY_FILENAME) |
---|
33 | |
---|
34 | install_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 | |
---|
160 | tor_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 | |
---|
166 | i2p_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 | |
---|
176 | if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency': |
---|
177 | del sys.argv[1] |
---|
178 | install_requires += ["fakedependency >= 1.0.0"] |
---|
179 | |
---|
180 | from setuptools import find_packages, setup |
---|
181 | from setuptools import Command |
---|
182 | from setuptools.command import install |
---|
183 | |
---|
184 | |
---|
185 | trove_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 | |
---|
217 | GIT_VERSION_BODY = ''' |
---|
218 | # This _version.py is generated from git metadata by the tahoe setup.py. |
---|
219 | |
---|
220 | __pkgname__ = "%(pkgname)s" |
---|
221 | real_version = "%(version)s" |
---|
222 | full_version = "%(full)s" |
---|
223 | branch = "%(branch)s" |
---|
224 | verstr = "%(normalized)s" |
---|
225 | __version__ = verstr |
---|
226 | ''' |
---|
227 | |
---|
228 | def 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 | |
---|
243 | def 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 | |
---|
309 | class 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 | ******************************************************************** |
---|
328 | Warning: 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 | |
---|
355 | class 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 | |
---|
366 | setup_args = {} |
---|
367 | if version: |
---|
368 | setup_args["version"] = version |
---|
369 | |
---|
370 | setup(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 | ) |
---|