2007-04-19 20:47:59 +00:00
|
|
|
"""
|
2007-04-30 20:06:09 +00:00
|
|
|
Decentralized storage grid.
|
2007-04-19 20:47:59 +00:00
|
|
|
|
2012-03-08 23:17:19 +00:00
|
|
|
community web site: U{https://tahoe-lafs.org/}
|
2007-04-19 20:47:59 +00:00
|
|
|
"""
|
|
|
|
|
2011-01-21 05:36:10 +00:00
|
|
|
class PackagingError(EnvironmentError):
|
|
|
|
"""
|
|
|
|
Raised when there is an error in packaging of Tahoe-LAFS or its
|
|
|
|
dependencies which makes it impossible to proceed safely.
|
|
|
|
"""
|
|
|
|
pass
|
2010-05-23 22:11:57 +00:00
|
|
|
|
2007-05-04 03:14:07 +00:00
|
|
|
__version__ = "unknown"
|
|
|
|
try:
|
2010-02-26 08:14:33 +00:00
|
|
|
from allmydata._version import __version__
|
2007-05-04 03:14:07 +00:00
|
|
|
except ImportError:
|
2013-04-25 01:14:50 +00:00
|
|
|
# We're running in a tree that hasn't run update_version, and didn't
|
|
|
|
# come with a _version.py, so we don't know what our version is.
|
|
|
|
# This should not happen very often.
|
|
|
|
pass
|
|
|
|
|
|
|
|
full_version = "unknown"
|
|
|
|
branch = "unknown"
|
|
|
|
try:
|
|
|
|
from allmydata._version import full_version, branch
|
|
|
|
except ImportError:
|
|
|
|
# We're running in a tree that hasn't run update_version, and didn't
|
|
|
|
# come with a _version.py, so we don't know what our full version or
|
|
|
|
# branch is. This should not happen very often.
|
2007-05-04 03:14:07 +00:00
|
|
|
pass
|
2007-04-19 20:47:59 +00:00
|
|
|
|
2016-08-08 01:46:59 +00:00
|
|
|
__appname__ = "tahoe-lafs"
|
versioning: include an "appname" in the application version string in the versioning protocol, and make that appname be controlled by setup.py
It is currently hardcoded in setup.py to be 'allmydata-tahoe'. Ticket #556 is to make it configurable by a runtime command-line argument to setup.py: "--appname=foo", but I suddenly wondered if we really wanted that and at the same time realized that we don't need that for tahoe-1.3.0 release, so this patch just hardcodes it in setup.py.
setup.py inspects a file named 'src/allmydata/_appname.py' and assert that it contains the string "__appname__ = 'allmydata-tahoe'", and creates it if it isn't already present. src/allmydata/__init__.py import _appname and reads __appname__ from it. The rest of the Python code imports allmydata and inspects "allmydata.__appname__", although actually every use it uses "allmydata.__full_version__" instead, where "allmydata.__full_version__" is created in src/allmydata/__init__.py to be:
__full_version__ = __appname + '-' + str(__version__).
All the code that emits an "application version string" when describing what version of a protocol it supports (introducer server, storage server, upload helper), or when describing itself in general (introducer client), usese allmydata.__full_version__.
This fixes ticket #556 at least well enough for tahoe-1.3.0 release.
2009-02-12 00:18:16 +00:00
|
|
|
|
2016-08-08 01:46:59 +00:00
|
|
|
# __full_version__ is the one that you ought to use when identifying yourself
|
|
|
|
# in the "application" part of the Tahoe versioning scheme:
|
2012-03-08 23:17:19 +00:00
|
|
|
# https://tahoe-lafs.org/trac/tahoe-lafs/wiki/Versioning
|
2009-02-13 05:37:38 +00:00
|
|
|
__full_version__ = __appname__ + '/' + str(__version__)
|
versioning: include an "appname" in the application version string in the versioning protocol, and make that appname be controlled by setup.py
It is currently hardcoded in setup.py to be 'allmydata-tahoe'. Ticket #556 is to make it configurable by a runtime command-line argument to setup.py: "--appname=foo", but I suddenly wondered if we really wanted that and at the same time realized that we don't need that for tahoe-1.3.0 release, so this patch just hardcodes it in setup.py.
setup.py inspects a file named 'src/allmydata/_appname.py' and assert that it contains the string "__appname__ = 'allmydata-tahoe'", and creates it if it isn't already present. src/allmydata/__init__.py import _appname and reads __appname__ from it. The rest of the Python code imports allmydata and inspects "allmydata.__appname__", although actually every use it uses "allmydata.__full_version__" instead, where "allmydata.__full_version__" is created in src/allmydata/__init__.py to be:
__full_version__ = __appname + '-' + str(__version__).
All the code that emits an "application version string" when describing what version of a protocol it supports (introducer server, storage server, upload helper), or when describing itself in general (introducer client), usese allmydata.__full_version__.
This fixes ticket #556 at least well enough for tahoe-1.3.0 release.
2009-02-12 00:18:16 +00:00
|
|
|
|
2011-04-10 15:57:05 +00:00
|
|
|
import os, platform, re, subprocess, sys, traceback
|
2008-09-23 17:14:31 +00:00
|
|
|
_distributor_id_cmdline_re = re.compile("(?:Distributor ID:)\s*(.*)", re.I)
|
|
|
|
_release_cmdline_re = re.compile("(?:Release:)\s*(.*)", re.I)
|
2008-09-23 16:28:58 +00:00
|
|
|
|
2008-09-23 17:14:31 +00:00
|
|
|
_distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I)
|
|
|
|
_release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I)
|
2008-08-28 22:04:54 +00:00
|
|
|
|
2008-09-24 18:09:22 +00:00
|
|
|
_distname = None
|
|
|
|
_version = None
|
|
|
|
|
2008-09-22 23:53:54 +00:00
|
|
|
def get_linux_distro():
|
|
|
|
""" Tries to determine the name of the Linux OS distribution name.
|
|
|
|
|
2008-09-23 17:14:31 +00:00
|
|
|
First, try to parse a file named "/etc/lsb-release". If it exists, and
|
|
|
|
contains the "DISTRIB_ID=" line and the "DISTRIB_RELEASE=" line, then return
|
2008-09-24 18:09:22 +00:00
|
|
|
the strings parsed from that file.
|
|
|
|
|
|
|
|
If that doesn't work, then invoke platform.dist().
|
2008-09-23 17:14:31 +00:00
|
|
|
|
2008-09-24 18:09:22 +00:00
|
|
|
If that doesn't work, then try to execute "lsb_release", as standardized in
|
|
|
|
2001:
|
2008-09-22 23:53:54 +00:00
|
|
|
|
|
|
|
http://refspecs.freestandards.org/LSB_1.0.0/gLSB/lsbrelease.html
|
|
|
|
|
|
|
|
The current version of the standard is here:
|
|
|
|
|
|
|
|
http://refspecs.freestandards.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic/lsbrelease.html
|
|
|
|
|
2008-09-23 17:14:31 +00:00
|
|
|
that lsb_release emitted, as strings.
|
2008-09-23 16:28:58 +00:00
|
|
|
|
2008-09-22 23:53:54 +00:00
|
|
|
Returns a tuple (distname,version). Distname is what LSB calls a
|
|
|
|
"distributor id", e.g. "Ubuntu". Version is what LSB calls a "release",
|
|
|
|
e.g. "8.04".
|
|
|
|
|
|
|
|
A version of this has been submitted to python as a patch for the standard
|
|
|
|
library module "platform":
|
|
|
|
|
|
|
|
http://bugs.python.org/issue3937
|
|
|
|
"""
|
2008-09-24 18:09:22 +00:00
|
|
|
global _distname,_version
|
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
2008-09-23 16:28:58 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
etclsbrel = open("/etc/lsb-release", "rU")
|
2008-09-23 16:55:51 +00:00
|
|
|
for line in etclsbrel:
|
|
|
|
m = _distributor_id_file_re.search(line)
|
2008-09-23 16:28:58 +00:00
|
|
|
if m:
|
|
|
|
_distname = m.group(1).strip()
|
2008-09-23 17:14:31 +00:00
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
2008-09-23 16:55:51 +00:00
|
|
|
m = _release_file_re.search(line)
|
2008-09-23 16:28:58 +00:00
|
|
|
if m:
|
|
|
|
_version = m.group(1).strip()
|
2008-09-23 17:14:31 +00:00
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
2008-09-23 16:28:58 +00:00
|
|
|
except EnvironmentError:
|
2010-02-26 06:21:51 +00:00
|
|
|
pass
|
2008-09-23 16:28:58 +00:00
|
|
|
|
2008-09-24 18:09:22 +00:00
|
|
|
(_distname, _version) = platform.dist()[:2]
|
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
|
|
|
|
Avoid Popen() of executables that don't exist
The stdlib 'subprocess' module in python-2.7.4 through 2.7.7 suffers
from http://bugs.python.org/issue18851 which causes unrelated file
descriptors to be closed when `subprocess.call()` fails the `exec()`,
such as when the executable being invoked does not actually exist. There
appears to be some randomness involved. This was fixed in python-2.7.8.
Tahoe's iputil.py uses subprocess.call on many different "ifconfig"-type
executables, most of which don't exist on any given platform (added in
git commit 8e31d66cd0b). This results in a lot of file-descriptor
closing, which (at least during unit tests) tends to clobber important
things like Tub TCP sockets. This seems to be the root cause behind
ticket:2121, in which normal code tries to close already-closed sockets,
crashing the unit tests. Since different platforms have different
ifconfigs, some platforms will experience more failed execs than others,
so this bug could easily behave differently on linux vs freebsd, as well
as working normally on python-2.7.8 or 2.7.4.
This patch inserts a guard to make sure that os.path.isfile() is true
before allowing Popen.call() to try executing the target. This ought to
be enough to avoid the bug. It changes both iputil.py and
allmydata.__init__ (which uses Popen for calling "lsb_release"), which
are all the places where 'subprocess' is used outside of unit tests.
Other potential fixes: use the 'subprocess32' module from PyPI (which is
a bug-free backport of the Python3 stdlib subprocess module, but would
introduce a new dependency), or require python >= 2.7.8 (but this would
rule out development/deployment on the current OS-X 10.9 release, which
ships with 2.7.5, as well as other distributions like Ubuntu 14.04 LTS).
I believe this closes ticket:2121, and given the apparent relationship
between 2121 and 2023, I think it also closes ticket:2023 (although
since 2023 doesn't have copies of the failing log files, it's hard to
tell). I'm hoping that this will tide us over until 1.11 is released, at
which point we can execute on the plan to remove iputil.py entirely by
changing the way that nodes learn their externally-facing IP address.
2014-09-11 20:13:42 +00:00
|
|
|
if os.path.isfile("/usr/bin/lsb_release") or os.path.isfile("/bin/lsb_release"):
|
|
|
|
try:
|
|
|
|
p = subprocess.Popen(["lsb_release", "--all"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
rc = p.wait()
|
|
|
|
if rc == 0:
|
|
|
|
for line in p.stdout.readlines():
|
|
|
|
m = _distributor_id_cmdline_re.search(line)
|
|
|
|
if m:
|
|
|
|
_distname = m.group(1).strip()
|
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
|
|
|
|
|
|
|
m = _release_cmdline_re.search(p.stdout.read())
|
|
|
|
if m:
|
|
|
|
_version = m.group(1).strip()
|
|
|
|
if _distname and _version:
|
|
|
|
return (_distname, _version)
|
|
|
|
except EnvironmentError:
|
|
|
|
pass
|
2008-09-23 16:28:58 +00:00
|
|
|
|
2008-11-25 16:51:18 +00:00
|
|
|
if os.path.exists("/etc/arch-release"):
|
|
|
|
return ("Arch_Linux", "")
|
|
|
|
|
2008-09-24 18:09:22 +00:00
|
|
|
return (_distname,_version)
|
2008-09-22 23:53:54 +00:00
|
|
|
|
|
|
|
def get_platform():
|
|
|
|
# Our version of platform.platform(), telling us both less and more than the
|
|
|
|
# Python Standard Library's version does.
|
|
|
|
# We omit details such as the Linux kernel version number, but we add a
|
|
|
|
# more detailed and correct rendition of the Linux distribution and
|
|
|
|
# distribution-version.
|
|
|
|
if "linux" in platform.system().lower():
|
|
|
|
return platform.system()+"-"+"_".join(get_linux_distro())+"-"+platform.machine()+"-"+"_".join([x for x in platform.architecture() if x])
|
|
|
|
else:
|
|
|
|
return platform.platform()
|
|
|
|
|
2010-08-01 16:05:17 +00:00
|
|
|
|
2011-01-21 05:36:10 +00:00
|
|
|
from allmydata.util import verlib
|
2011-04-01 20:27:50 +00:00
|
|
|
def normalized_version(verstr, what=None):
|
|
|
|
try:
|
2015-09-11 23:59:51 +00:00
|
|
|
suggested = verlib.suggest_normalized_version(verstr) or verstr
|
|
|
|
return verlib.NormalizedVersion(suggested)
|
2016-02-23 20:07:38 +00:00
|
|
|
except verlib.IrrationalVersionError:
|
|
|
|
raise
|
|
|
|
except StandardError:
|
2011-04-10 15:57:05 +00:00
|
|
|
cls, value, trace = sys.exc_info()
|
2011-04-01 20:27:50 +00:00
|
|
|
raise PackagingError, ("could not parse %s due to %s: %s"
|
2011-04-10 15:57:05 +00:00
|
|
|
% (what or repr(verstr), cls.__name__, value)), trace
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2015-06-02 17:07:20 +00:00
|
|
|
def get_openssl_version():
|
|
|
|
try:
|
|
|
|
from OpenSSL import SSL
|
|
|
|
return extract_openssl_version(SSL)
|
|
|
|
except Exception:
|
|
|
|
return ("unknown", None, None)
|
|
|
|
|
|
|
|
def extract_openssl_version(ssl_module):
|
|
|
|
openssl_version = ssl_module.SSLeay_version(ssl_module.SSLEAY_VERSION)
|
|
|
|
if openssl_version.startswith('OpenSSL '):
|
|
|
|
openssl_version = openssl_version[8 :]
|
|
|
|
|
|
|
|
(version, _, comment) = openssl_version.partition(' ')
|
|
|
|
|
|
|
|
try:
|
|
|
|
openssl_cflags = ssl_module.SSLeay_version(ssl_module.SSLEAY_CFLAGS)
|
|
|
|
if '-DOPENSSL_NO_HEARTBEATS' in openssl_cflags.split(' '):
|
|
|
|
comment += ", no heartbeats"
|
|
|
|
except Exception:
|
|
|
|
pass
|
|
|
|
|
|
|
|
return (version, None, comment if comment else None)
|
2010-08-08 17:12:35 +00:00
|
|
|
|
2009-01-19 22:04:35 +00:00
|
|
|
def get_package_versions_and_locations():
|
2011-01-21 05:36:10 +00:00
|
|
|
import warnings
|
2012-06-14 21:23:08 +00:00
|
|
|
from _auto_deps import package_imports, global_deprecation_messages, deprecation_messages, \
|
2015-07-28 22:41:13 +00:00
|
|
|
runtime_warning_messages, warning_imports, ignorable
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
def package_dir(srcfile):
|
|
|
|
return os.path.dirname(os.path.dirname(os.path.normcase(os.path.realpath(srcfile))))
|
|
|
|
|
|
|
|
# pkg_resources.require returns the distribution that pkg_resources attempted to put
|
|
|
|
# on sys.path, which can differ from the one that we actually import due to #1258,
|
|
|
|
# or any other bug that causes sys.path to be set up incorrectly. Therefore we
|
|
|
|
# must import the packages in order to check their versions and paths.
|
|
|
|
|
2014-09-01 18:39:57 +00:00
|
|
|
# This is to suppress all UserWarnings and various DeprecationWarnings and RuntimeWarnings
|
2012-06-14 21:23:08 +00:00
|
|
|
# (listed in _auto_deps.py).
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2014-09-01 18:39:57 +00:00
|
|
|
warnings.filterwarnings("ignore", category=UserWarning, append=True)
|
|
|
|
|
2012-06-14 21:23:08 +00:00
|
|
|
for msg in global_deprecation_messages + deprecation_messages:
|
2011-01-21 05:36:10 +00:00
|
|
|
warnings.filterwarnings("ignore", category=DeprecationWarning, message=msg, append=True)
|
2012-05-14 03:23:52 +00:00
|
|
|
for msg in runtime_warning_messages:
|
|
|
|
warnings.filterwarnings("ignore", category=RuntimeWarning, message=msg, append=True)
|
2009-06-04 15:37:28 +00:00
|
|
|
try:
|
2011-08-18 04:07:49 +00:00
|
|
|
for modulename in warning_imports:
|
2011-01-21 05:36:10 +00:00
|
|
|
try:
|
|
|
|
__import__(modulename)
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
finally:
|
2014-09-01 18:39:57 +00:00
|
|
|
# Leave suppressions for UserWarnings and global_deprecation_messages active.
|
2017-12-30 23:19:41 +00:00
|
|
|
for _ in runtime_warning_messages + deprecation_messages:
|
2011-08-18 04:07:49 +00:00
|
|
|
warnings.filters.pop()
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
packages = []
|
2016-02-23 20:08:31 +00:00
|
|
|
pkg_resources_vers_and_locs = dict()
|
|
|
|
|
|
|
|
if not hasattr(sys, 'frozen'):
|
|
|
|
import pkg_resources
|
|
|
|
from _auto_deps import install_requires
|
|
|
|
|
|
|
|
pkg_resources_vers_and_locs = dict([(p.project_name.lower(), (str(p.version), p.location))
|
|
|
|
for p in pkg_resources.require(install_requires)])
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2014-12-26 22:29:42 +00:00
|
|
|
def get_version(module):
|
|
|
|
if hasattr(module, '__version__'):
|
|
|
|
return str(getattr(module, '__version__'))
|
|
|
|
elif hasattr(module, 'version'):
|
|
|
|
ver = getattr(module, 'version')
|
|
|
|
if isinstance(ver, tuple):
|
|
|
|
return '.'.join(map(str, ver))
|
|
|
|
else:
|
|
|
|
return str(ver)
|
|
|
|
else:
|
|
|
|
return 'unknown'
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
for pkgname, modulename in [(__appname__, 'allmydata')] + package_imports:
|
|
|
|
if modulename:
|
|
|
|
try:
|
|
|
|
__import__(modulename)
|
|
|
|
module = sys.modules[modulename]
|
|
|
|
except ImportError:
|
2011-04-10 15:57:05 +00:00
|
|
|
etype, emsg, etrace = sys.exc_info()
|
2011-04-11 19:07:38 +00:00
|
|
|
trace_info = (etype, str(emsg), ([None] + traceback.extract_tb(etrace))[-1])
|
2011-04-10 15:57:05 +00:00
|
|
|
packages.append( (pkgname, (None, None, trace_info)) )
|
2011-01-21 05:36:10 +00:00
|
|
|
else:
|
2012-05-16 02:47:25 +00:00
|
|
|
comment = None
|
2013-04-25 01:14:50 +00:00
|
|
|
if pkgname == __appname__:
|
|
|
|
comment = "%s: %s" % (branch, full_version)
|
|
|
|
elif pkgname == 'setuptools' and hasattr(module, '_distribute'):
|
2012-05-16 02:47:25 +00:00
|
|
|
# distribute does not report its version in any module variables
|
|
|
|
comment = 'distribute'
|
2016-02-23 20:08:31 +00:00
|
|
|
ver = get_version(module)
|
|
|
|
loc = package_dir(module.__file__)
|
|
|
|
if ver == "unknown" and pkgname in pkg_resources_vers_and_locs:
|
|
|
|
(pr_ver, pr_loc) = pkg_resources_vers_and_locs[pkgname]
|
|
|
|
if loc == os.path.normcase(os.path.realpath(pr_loc)):
|
|
|
|
ver = pr_ver
|
|
|
|
packages.append( (pkgname, (ver, loc, comment)) )
|
2011-01-21 05:36:10 +00:00
|
|
|
elif pkgname == 'python':
|
2011-01-28 05:41:50 +00:00
|
|
|
packages.append( (pkgname, (platform.python_version(), sys.executable, None)) )
|
2011-01-21 05:36:10 +00:00
|
|
|
elif pkgname == 'platform':
|
2011-01-28 05:41:50 +00:00
|
|
|
packages.append( (pkgname, (get_platform(), None, None)) )
|
2015-06-02 17:07:20 +00:00
|
|
|
elif pkgname == 'OpenSSL':
|
|
|
|
packages.append( (pkgname, get_openssl_version()) )
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
cross_check_errors = []
|
|
|
|
|
2016-02-23 20:08:31 +00:00
|
|
|
if len(pkg_resources_vers_and_locs) > 0:
|
2015-07-28 22:41:13 +00:00
|
|
|
imported_packages = set([p.lower() for (p, _) in packages])
|
|
|
|
extra_packages = []
|
|
|
|
|
|
|
|
for pr_name, (pr_ver, pr_loc) in pkg_resources_vers_and_locs.iteritems():
|
|
|
|
if pr_name not in imported_packages and pr_name not in ignorable:
|
|
|
|
extra_packages.append( (pr_name, (pr_ver, pr_loc, "according to pkg_resources")) )
|
|
|
|
|
|
|
|
cross_check_errors = cross_check(pkg_resources_vers_and_locs, packages)
|
|
|
|
packages += extra_packages
|
|
|
|
|
|
|
|
return packages, cross_check_errors
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
|
2019-01-23 19:58:44 +00:00
|
|
|
def split_requirement(req):
|
|
|
|
"""
|
|
|
|
Split up a single requirement string into the different version constraint pieces.
|
|
|
|
|
|
|
|
This is like req.split(",") except it doesn't split on , found inside [].
|
|
|
|
|
|
|
|
:return: A list of the split up pieces.
|
|
|
|
"""
|
|
|
|
in_extras = False
|
|
|
|
pieces = []
|
|
|
|
chunk = ''
|
|
|
|
for ch in req:
|
|
|
|
if in_extras:
|
|
|
|
if ch == ']':
|
|
|
|
in_extras = False
|
|
|
|
chunk += ch
|
|
|
|
else:
|
|
|
|
if ch == '[':
|
|
|
|
in_extras = True
|
|
|
|
chunk += ch
|
|
|
|
elif ch == ',':
|
|
|
|
pieces.append(chunk)
|
|
|
|
chunk = ''
|
|
|
|
else:
|
|
|
|
chunk += ch
|
|
|
|
pieces.append(chunk)
|
|
|
|
return pieces
|
|
|
|
|
|
|
|
|
2011-01-21 05:36:10 +00:00
|
|
|
def check_requirement(req, vers_and_locs):
|
2015-01-20 18:52:02 +00:00
|
|
|
# We support only conjunctions of <=, >=, and !=
|
2019-01-23 19:58:44 +00:00
|
|
|
reqlist = split_requirement(req)
|
2015-01-20 18:52:02 +00:00
|
|
|
name = reqlist[0].split('<=')[0].split('>=')[0].split('!=')[0].strip(' ').split('[')[0]
|
2011-01-21 05:36:10 +00:00
|
|
|
if name not in vers_and_locs:
|
|
|
|
raise PackagingError("no version info for %s" % (name,))
|
|
|
|
if req.strip(' ') == name:
|
|
|
|
return
|
2011-01-28 05:41:50 +00:00
|
|
|
(actual, location, comment) = vers_and_locs[name]
|
2011-01-21 05:36:10 +00:00
|
|
|
if actual is None:
|
2011-04-11 19:07:38 +00:00
|
|
|
# comment is (type, message, (filename, line number, function name, text)) for the original ImportError
|
2011-04-10 15:57:05 +00:00
|
|
|
raise ImportError("for requirement %r: %s" % (req, comment))
|
2011-01-21 05:36:10 +00:00
|
|
|
if actual == 'unknown':
|
|
|
|
return
|
2016-02-23 20:07:38 +00:00
|
|
|
try:
|
|
|
|
actualver = normalized_version(actual, what="actual version %r of %s from %r" %
|
|
|
|
(actual, name, location))
|
|
|
|
matched = match_requirement(req, reqlist, actualver)
|
|
|
|
except verlib.IrrationalVersionError:
|
|
|
|
# meh, it probably doesn't matter
|
|
|
|
return
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2016-02-23 20:07:38 +00:00
|
|
|
if not matched:
|
2015-01-20 18:52:02 +00:00
|
|
|
msg = ("We require %s, but could only find version %s.\n" % (req, actual))
|
|
|
|
if location and location != 'unknown':
|
|
|
|
msg += "The version we found is from %r.\n" % (location,)
|
|
|
|
msg += ("To resolve this problem, uninstall that version, either using your\n"
|
|
|
|
"operating system's package manager or by moving aside the directory.")
|
|
|
|
raise PackagingError(msg)
|
|
|
|
|
|
|
|
|
|
|
|
def match_requirement(req, reqlist, actualver):
|
2011-01-21 05:36:10 +00:00
|
|
|
for r in reqlist:
|
2011-08-15 03:51:53 +00:00
|
|
|
s = r.split('<=')
|
2011-01-21 05:36:10 +00:00
|
|
|
if len(s) == 2:
|
|
|
|
required = s[1].strip(' ')
|
2015-01-20 18:52:02 +00:00
|
|
|
if not (actualver <= normalized_version(required, what="required maximum version %r in %r" % (required, req))):
|
|
|
|
return False # maximum requirement not met
|
2009-06-04 15:37:28 +00:00
|
|
|
else:
|
2011-08-15 03:51:53 +00:00
|
|
|
s = r.split('>=')
|
2011-01-21 05:36:10 +00:00
|
|
|
if len(s) == 2:
|
|
|
|
required = s[1].strip(' ')
|
2015-01-20 18:52:02 +00:00
|
|
|
if not (actualver >= normalized_version(required, what="required minimum version %r in %r" % (required, req))):
|
|
|
|
return False # minimum requirement not met
|
2011-01-21 05:36:10 +00:00
|
|
|
else:
|
2015-01-20 18:52:02 +00:00
|
|
|
s = r.split('!=')
|
2011-08-15 03:51:53 +00:00
|
|
|
if len(s) == 2:
|
|
|
|
required = s[1].strip(' ')
|
2015-01-20 18:52:02 +00:00
|
|
|
if not (actualver != normalized_version(required, what="excluded version %r in %r" % (required, req))):
|
|
|
|
return False # not-equal requirement not met
|
2011-08-15 03:51:53 +00:00
|
|
|
else:
|
|
|
|
raise PackagingError("no version info or could not understand requirement %r" % (req,))
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2015-01-20 18:52:02 +00:00
|
|
|
return True
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
|
2011-02-21 01:58:17 +00:00
|
|
|
def cross_check(pkg_resources_vers_and_locs, imported_vers_and_locs_list):
|
|
|
|
"""This function returns a list of errors due to any failed cross-checks."""
|
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
from _auto_deps import not_import_versionable
|
2014-09-25 17:54:14 +00:00
|
|
|
|
2011-01-21 05:36:10 +00:00
|
|
|
errors = []
|
2015-06-02 17:07:20 +00:00
|
|
|
not_pkg_resourceable = ['python', 'platform', __appname__.lower(), 'openssl']
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2011-02-21 01:58:17 +00:00
|
|
|
for name, (imp_ver, imp_loc, imp_comment) in imported_vers_and_locs_list:
|
2011-01-21 05:36:10 +00:00
|
|
|
name = name.lower()
|
|
|
|
if name not in not_pkg_resourceable:
|
|
|
|
if name not in pkg_resources_vers_and_locs:
|
2011-01-28 05:41:50 +00:00
|
|
|
if name == "setuptools" and "distribute" in pkg_resources_vers_and_locs:
|
|
|
|
pr_ver, pr_loc = pkg_resources_vers_and_locs["distribute"]
|
|
|
|
if not (os.path.normpath(os.path.realpath(pr_loc)) == os.path.normpath(os.path.realpath(imp_loc))
|
|
|
|
and imp_comment == "distribute"):
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: dependency 'setuptools' found to be version %r of 'distribute' from %r "
|
|
|
|
"by pkg_resources, but 'import setuptools' gave version %r [%s] from %r. "
|
|
|
|
"A version mismatch is expected, but a location mismatch is not."
|
2011-01-28 05:41:50 +00:00
|
|
|
% (pr_ver, pr_loc, imp_ver, imp_comment or 'probably *not* distribute', imp_loc))
|
|
|
|
else:
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: dependency %r (version %r imported from %r) was not found by pkg_resources."
|
2011-01-28 05:41:50 +00:00
|
|
|
% (name, imp_ver, imp_loc))
|
|
|
|
continue
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
pr_ver, pr_loc = pkg_resources_vers_and_locs[name]
|
2012-09-14 02:14:48 +00:00
|
|
|
if imp_ver is None and imp_loc is None:
|
|
|
|
errors.append("Warning: dependency %r could not be imported. pkg_resources thought it should be possible "
|
|
|
|
"to import version %r from %r.\nThe exception trace was %r."
|
|
|
|
% (name, pr_ver, pr_loc, imp_comment))
|
|
|
|
continue
|
|
|
|
|
2015-09-11 23:59:51 +00:00
|
|
|
# If the pkg_resources version is identical to the imported version, don't attempt
|
|
|
|
# to normalize them, since it is unnecessary and may fail (ticket #2499).
|
|
|
|
if imp_ver != 'unknown' and pr_ver == imp_ver:
|
|
|
|
continue
|
|
|
|
|
2011-01-21 05:36:10 +00:00
|
|
|
try:
|
|
|
|
pr_normver = normalized_version(pr_ver)
|
2016-02-23 20:07:38 +00:00
|
|
|
except verlib.IrrationalVersionError:
|
|
|
|
continue
|
2019-03-28 11:45:28 +00:00
|
|
|
except Exception as e:
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: version number %r found for dependency %r by pkg_resources could not be parsed. "
|
|
|
|
"The version found by import was %r from %r. "
|
2011-01-21 05:36:10 +00:00
|
|
|
"pkg_resources thought it should be found at %r. "
|
|
|
|
"The exception was %s: %s"
|
2011-02-21 02:01:25 +00:00
|
|
|
% (pr_ver, name, imp_ver, imp_loc, pr_loc, e.__class__.__name__, e))
|
2011-01-21 05:36:10 +00:00
|
|
|
else:
|
|
|
|
if imp_ver == 'unknown':
|
|
|
|
if name not in not_import_versionable:
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: unexpectedly could not find a version number for dependency %r imported from %r. "
|
|
|
|
"pkg_resources thought it should be version %r at %r."
|
2011-01-21 05:36:10 +00:00
|
|
|
% (name, imp_loc, pr_ver, pr_loc))
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
imp_normver = normalized_version(imp_ver)
|
2016-02-23 20:07:38 +00:00
|
|
|
except verlib.IrrationalVersionError:
|
|
|
|
continue
|
2019-03-28 11:45:28 +00:00
|
|
|
except Exception as e:
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: version number %r found for dependency %r (imported from %r) could not be parsed. "
|
|
|
|
"pkg_resources thought it should be version %r at %r. "
|
2011-01-21 05:36:10 +00:00
|
|
|
"The exception was %s: %s"
|
2011-02-21 02:01:25 +00:00
|
|
|
% (imp_ver, name, imp_loc, pr_ver, pr_loc, e.__class__.__name__, e))
|
2011-01-21 05:36:10 +00:00
|
|
|
else:
|
|
|
|
if pr_ver == 'unknown' or (pr_normver != imp_normver):
|
|
|
|
if not os.path.normpath(os.path.realpath(pr_loc)) == os.path.normpath(os.path.realpath(imp_loc)):
|
2011-02-21 02:01:25 +00:00
|
|
|
errors.append("Warning: dependency %r found to have version number %r (normalized to %r, from %r) "
|
|
|
|
"by pkg_resources, but version %r (normalized to %r, from %r) by import."
|
2011-01-21 05:36:10 +00:00
|
|
|
% (name, pr_ver, str(pr_normver), pr_loc, imp_ver, str(imp_normver), imp_loc))
|
|
|
|
|
2015-07-28 16:35:11 +00:00
|
|
|
return errors
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
_vers_and_locs_list, _cross_check_errors = get_package_versions_and_locations()
|
|
|
|
|
|
|
|
|
2011-01-22 04:02:20 +00:00
|
|
|
def get_error_string(errors, debug=False):
|
2011-01-21 05:36:10 +00:00
|
|
|
from allmydata._auto_deps import install_requires
|
|
|
|
|
2011-01-22 04:02:20 +00:00
|
|
|
msg = "\n%s\n" % ("\n".join(errors),)
|
|
|
|
if debug:
|
|
|
|
msg += ("\n"
|
|
|
|
"For debugging purposes, the PYTHONPATH was\n"
|
|
|
|
" %r\n"
|
|
|
|
"install_requires was\n"
|
|
|
|
" %r\n"
|
|
|
|
"sys.path after importing pkg_resources was\n"
|
|
|
|
" %s\n"
|
|
|
|
% (os.environ.get('PYTHONPATH'), install_requires, (os.pathsep+"\n ").join(sys.path)) )
|
|
|
|
return msg
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
def check_all_requirements():
|
|
|
|
"""This function returns a list of errors due to any failed checks."""
|
|
|
|
|
|
|
|
from allmydata._auto_deps import install_requires
|
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
fatal_errors = []
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2013-03-15 05:13:30 +00:00
|
|
|
# We require at least 2.6 on all platforms.
|
2012-05-16 02:41:49 +00:00
|
|
|
# (On Python 3, we'll have failed long before this point.)
|
2013-03-15 05:13:30 +00:00
|
|
|
if sys.version_info < (2, 6):
|
2012-05-16 02:41:49 +00:00
|
|
|
try:
|
|
|
|
version_string = ".".join(map(str, sys.version_info))
|
|
|
|
except Exception:
|
|
|
|
version_string = repr(sys.version_info)
|
2015-07-28 22:41:13 +00:00
|
|
|
fatal_errors.append("Tahoe-LAFS currently requires Python v2.6 or greater (but less than v3), not %s"
|
|
|
|
% (version_string,))
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
vers_and_locs = dict(_vers_and_locs_list)
|
|
|
|
for requirement in install_requires:
|
|
|
|
try:
|
|
|
|
check_requirement(requirement, vers_and_locs)
|
2019-03-28 11:45:28 +00:00
|
|
|
except (ImportError, PackagingError) as e:
|
2015-07-28 22:41:13 +00:00
|
|
|
fatal_errors.append("%s: %s" % (e.__class__.__name__, e))
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
if fatal_errors:
|
|
|
|
raise PackagingError(get_error_string(fatal_errors + _cross_check_errors, debug=True))
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
check_all_requirements()
|
2009-01-19 22:04:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_package_versions():
|
2011-01-28 05:41:50 +00:00
|
|
|
return dict([(k, v) for k, (v, l, c) in _vers_and_locs_list])
|
2009-01-19 22:04:35 +00:00
|
|
|
|
2009-01-16 19:47:51 +00:00
|
|
|
def get_package_locations():
|
2011-01-28 05:41:50 +00:00
|
|
|
return dict([(k, l) for k, (v, l, c) in _vers_and_locs_list])
|
2009-01-16 19:47:51 +00:00
|
|
|
|
2011-01-22 04:02:20 +00:00
|
|
|
def get_package_versions_string(show_paths=False, debug=False):
|
2007-12-13 02:37:37 +00:00
|
|
|
res = []
|
2011-01-28 05:41:50 +00:00
|
|
|
for p, (v, loc, comment) in _vers_and_locs_list:
|
2009-01-19 22:04:35 +00:00
|
|
|
info = str(p) + ": " + str(v)
|
2011-01-28 05:41:50 +00:00
|
|
|
if comment:
|
|
|
|
info = info + " [%s]" % str(comment)
|
2009-01-19 22:04:35 +00:00
|
|
|
if show_paths:
|
|
|
|
info = info + " (%s)" % str(loc)
|
|
|
|
res.append(info)
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2012-06-26 16:18:45 +00:00
|
|
|
output = "\n".join(res) + "\n"
|
2011-01-21 05:36:10 +00:00
|
|
|
|
2015-07-28 22:41:13 +00:00
|
|
|
if _cross_check_errors:
|
|
|
|
output += get_error_string(_cross_check_errors, debug=debug)
|
2011-01-21 05:36:10 +00:00
|
|
|
|
|
|
|
return output
|