Merge pull request #1344 from tahoe-lafs/3072-python-3.12-support
Python 3.12 support, compatability with Eliot 1.15 Fixes ticket:4071
This commit is contained in:
commit
e3c8ff0fc6
|
@ -46,8 +46,8 @@ export PIP_FIND_LINKS="file://${WHEELHOUSE_PATH}"
|
||||||
# setuptools 45 requires Python 3.5 or newer. Even though we upgraded pip
|
# setuptools 45 requires Python 3.5 or newer. Even though we upgraded pip
|
||||||
# above, it may still not be able to get us a compatible version unless we
|
# above, it may still not be able to get us a compatible version unless we
|
||||||
# explicitly ask for one.
|
# explicitly ask for one.
|
||||||
"${PIP}" install --upgrade setuptools==44.0.0 wheel
|
"${PIP}" install --upgrade setuptools wheel
|
||||||
|
|
||||||
# Just about every user of this image wants to use tox from the bootstrap
|
# Just about every user of this image wants to use tox from the bootstrap
|
||||||
# virtualenv so go ahead and install it now.
|
# virtualenv so go ahead and install it now.
|
||||||
"${PIP}" install "tox~=3.0"
|
"${PIP}" install "tox~=4.0"
|
||||||
|
|
|
@ -45,16 +45,17 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# On macOS don't bother with 3.8, just to get faster builds.
|
|
||||||
- os: macos-12
|
- os: macos-12
|
||||||
python-version: "3.9"
|
python-version: "3.12"
|
||||||
- os: macos-12
|
|
||||||
python-version: "3.11"
|
|
||||||
# We only support PyPy on Linux at the moment.
|
# We only support PyPy on Linux at the moment.
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python-version: "pypy-3.8"
|
python-version: "pypy-3.8"
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
python-version: "pypy-3.9"
|
python-version: "pypy-3.9"
|
||||||
|
- os: ubuntu-latest
|
||||||
|
python-version: "3.12"
|
||||||
|
- os: windows-latest
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# See https://github.com/actions/checkout. A fetch-depth of 0
|
# See https://github.com/actions/checkout. A fetch-depth of 0
|
||||||
|
@ -72,7 +73,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install Python packages
|
- name: Install Python packages
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade "tox<4" tox-gh-actions setuptools
|
pip install --upgrade tox tox-gh-actions setuptools
|
||||||
pip list
|
pip list
|
||||||
|
|
||||||
- name: Display tool versions
|
- name: Display tool versions
|
||||||
|
@ -169,7 +170,7 @@ jobs:
|
||||||
- false
|
- false
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
python-version: "3.10"
|
python-version: "3.12"
|
||||||
force-foolscap: true
|
force-foolscap: true
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
@ -204,7 +205,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install Python packages
|
- name: Install Python packages
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade "tox<4"
|
pip install --upgrade tox
|
||||||
pip list
|
pip list
|
||||||
|
|
||||||
- name: Display tool versions
|
- name: Display tool versions
|
||||||
|
@ -264,7 +265,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install Python packages
|
- name: Install Python packages
|
||||||
run: |
|
run: |
|
||||||
pip install --upgrade "tox<4"
|
pip install --upgrade tox
|
||||||
pip list
|
pip list
|
||||||
|
|
||||||
- name: Display tool versions
|
- name: Display tool versions
|
||||||
|
|
|
@ -30,6 +30,7 @@ from allmydata.util.deferredutil import async_to_deferred
|
||||||
if sys.platform.startswith('win'):
|
if sys.platform.startswith('win'):
|
||||||
pytest.skip('Skipping Tor tests on Windows', allow_module_level=True)
|
pytest.skip('Skipping Tor tests on Windows', allow_module_level=True)
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info[:2] > (3, 11), reason='Chutney still does not support 3.12')
|
||||||
@pytest_twisted.inlineCallbacks
|
@pytest_twisted.inlineCallbacks
|
||||||
def test_onion_service_storage(reactor, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl):
|
def test_onion_service_storage(reactor, request, temp_dir, flog_gatherer, tor_network, tor_introducer_furl):
|
||||||
"""
|
"""
|
||||||
|
@ -140,6 +141,7 @@ def _create_anonymous_node(reactor, name, web_port, request, temp_dir, flog_gath
|
||||||
print("okay, launched")
|
print("okay, launched")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info[:2] > (3, 11), reason='Chutney still does not support 3.12')
|
||||||
@pytest.mark.skipif(sys.platform.startswith('darwin'), reason='This test has issues on macOS')
|
@pytest.mark.skipif(sys.platform.startswith('darwin'), reason='This test has issues on macOS')
|
||||||
@pytest_twisted.inlineCallbacks
|
@pytest_twisted.inlineCallbacks
|
||||||
def test_anonymous_client(reactor, request, temp_dir, flog_gatherer, tor_network, introducer_furl):
|
def test_anonymous_client(reactor, request, temp_dir, flog_gatherer, tor_network, introducer_furl):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added support for Python 3.12, and work with Eliot 1.15
|
|
@ -0,0 +1,3 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
15
setup.py
15
setup.py
|
@ -112,7 +112,7 @@ install_requires = [
|
||||||
"magic-wormhole >= 0.10.2",
|
"magic-wormhole >= 0.10.2",
|
||||||
|
|
||||||
# We want a new enough version to support custom JSON encoders.
|
# We want a new enough version to support custom JSON encoders.
|
||||||
"eliot < 1.15.0", # temporary cap, to be fixed in PR #1344
|
"eliot >= 1.14.0",
|
||||||
|
|
||||||
"pyrsistent",
|
"pyrsistent",
|
||||||
|
|
||||||
|
@ -157,10 +157,6 @@ install_requires = [
|
||||||
"filelock",
|
"filelock",
|
||||||
]
|
]
|
||||||
|
|
||||||
setup_requires = [
|
|
||||||
'setuptools >= 28.8.0', # for PEP-440 style versions
|
|
||||||
]
|
|
||||||
|
|
||||||
tor_requires = [
|
tor_requires = [
|
||||||
# 23.5 added support for custom TLS contexts in web_agent(), which is
|
# 23.5 added support for custom TLS contexts in web_agent(), which is
|
||||||
# needed for the HTTP storage client to run over Tor.
|
# needed for the HTTP storage client to run over Tor.
|
||||||
|
@ -384,8 +380,8 @@ setup(name="tahoe-lafs", # also set in __init__.py
|
||||||
package_dir = {'':'src'},
|
package_dir = {'':'src'},
|
||||||
packages=find_packages('src') + ['allmydata.test.plugins'],
|
packages=find_packages('src') + ['allmydata.test.plugins'],
|
||||||
classifiers=trove_classifiers,
|
classifiers=trove_classifiers,
|
||||||
# We support Python 3.8 or later, 3.12 is untested for now
|
# We support Python 3.8 or later, 3.13 is untested for now
|
||||||
python_requires=">=3.8, <3.12",
|
python_requires=">=3.8, <3.13",
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
extras_require={
|
extras_require={
|
||||||
# Duplicate the Twisted pywin32 dependency here. See
|
# Duplicate the Twisted pywin32 dependency here. See
|
||||||
|
@ -409,8 +405,8 @@ setup(name="tahoe-lafs", # also set in __init__.py
|
||||||
# selected here are just the current versions at the time.
|
# selected here are just the current versions at the time.
|
||||||
# Bumping them to keep up with future releases is fine as long
|
# Bumping them to keep up with future releases is fine as long
|
||||||
# as those releases are known to actually work.
|
# as those releases are known to actually work.
|
||||||
"pip==22.0.3",
|
"pip==23.3.1",
|
||||||
"wheel==0.37.1",
|
"wheel==0.41.3",
|
||||||
"subunitreporter==23.8.0",
|
"subunitreporter==23.8.0",
|
||||||
"python-subunit==1.4.2",
|
"python-subunit==1.4.2",
|
||||||
"junitxml==0.7",
|
"junitxml==0.7",
|
||||||
|
@ -446,7 +442,6 @@ setup(name="tahoe-lafs", # also set in __init__.py
|
||||||
"allmydata": ["ported-modules.txt"],
|
"allmydata": ["ported-modules.txt"],
|
||||||
},
|
},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
setup_requires=setup_requires,
|
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'tahoe = allmydata.scripts.runner:run',
|
'tahoe = allmydata.scripts.runner:run',
|
||||||
|
|
|
@ -3,16 +3,6 @@ Decentralized storage grid.
|
||||||
|
|
||||||
community web site: U{https://tahoe-lafs.org/}
|
community web site: U{https://tahoe-lafs.org/}
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from future.utils import PY2, PY3
|
|
||||||
if PY2:
|
|
||||||
# Don't import future str() so we don't break Foolscap serialization on Python 2.
|
|
||||||
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, max, min # noqa: F401
|
|
||||||
from past.builtins import unicode as str
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"__version__",
|
"__version__",
|
||||||
|
@ -52,12 +42,6 @@ __appname__ = "tahoe-lafs"
|
||||||
# https://tahoe-lafs.org/trac/tahoe-lafs/wiki/Versioning
|
# https://tahoe-lafs.org/trac/tahoe-lafs/wiki/Versioning
|
||||||
__full_version__ = __appname__ + '/' + str(__version__)
|
__full_version__ = __appname__ + '/' + str(__version__)
|
||||||
|
|
||||||
|
|
||||||
# Install Python 3 module locations in Python 2:
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
|
|
||||||
|
|
||||||
# Monkey-patch 3rd party libraries:
|
# Monkey-patch 3rd party libraries:
|
||||||
from ._monkeypatch import patch
|
from ._monkeypatch import patch
|
||||||
patch()
|
patch()
|
||||||
|
@ -72,8 +56,7 @@ del patch
|
||||||
#
|
#
|
||||||
# Also note that BytesWarnings only happen if Python is run with -b option, so
|
# Also note that BytesWarnings only happen if Python is run with -b option, so
|
||||||
# in practice this should only affect tests.
|
# in practice this should only affect tests.
|
||||||
if PY3:
|
import warnings
|
||||||
import warnings
|
# Error on BytesWarnings, to catch things like str(b""), but only for
|
||||||
# Error on BytesWarnings, to catch things like str(b""), but only for
|
# allmydata code.
|
||||||
# allmydata code.
|
warnings.filterwarnings("error", category=BytesWarning, module=".*allmydata.*")
|
||||||
warnings.filterwarnings("error", category=BytesWarning, module=".*allmydata.*")
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from base64 import urlsafe_b64encode
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from configparser import NoSectionError
|
from configparser import NoSectionError
|
||||||
|
|
||||||
|
from six import ensure_text
|
||||||
from foolscap.furl import (
|
from foolscap.furl import (
|
||||||
decode_furl,
|
decode_furl,
|
||||||
)
|
)
|
||||||
|
@ -989,6 +990,9 @@ class _Client(node.Node, pollmixin.PollMixin):
|
||||||
static_servers = servers_yaml.get("storage", {})
|
static_servers = servers_yaml.get("storage", {})
|
||||||
log.msg("found %d static servers in private/servers.yaml" %
|
log.msg("found %d static servers in private/servers.yaml" %
|
||||||
len(static_servers))
|
len(static_servers))
|
||||||
|
static_servers = {
|
||||||
|
ensure_text(key): value for (key, value) in static_servers.items()
|
||||||
|
}
|
||||||
self.storage_broker.set_static_servers(static_servers)
|
self.storage_broker.set_static_servers(static_servers)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -78,7 +78,7 @@ _READONLY_PEERS = Field(
|
||||||
|
|
||||||
def _serialize_existing_shares(existing_shares):
|
def _serialize_existing_shares(existing_shares):
|
||||||
return {
|
return {
|
||||||
server: list(shares)
|
ensure_str(server): list(shares)
|
||||||
for (server, shares)
|
for (server, shares)
|
||||||
in existing_shares.items()
|
in existing_shares.items()
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ _EXISTING_SHARES = Field(
|
||||||
|
|
||||||
def _serialize_happiness_mappings(happiness_mappings):
|
def _serialize_happiness_mappings(happiness_mappings):
|
||||||
return {
|
return {
|
||||||
sharenum: base32.b2a(serverid)
|
str(sharenum): ensure_str(base32.b2a(serverid))
|
||||||
for (sharenum, serverid)
|
for (sharenum, serverid)
|
||||||
in happiness_mappings.items()
|
in happiness_mappings.items()
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ _UPLOAD_TRACKERS = Field(
|
||||||
u"upload_trackers",
|
u"upload_trackers",
|
||||||
lambda trackers: list(
|
lambda trackers: list(
|
||||||
dict(
|
dict(
|
||||||
server=tracker.get_name(),
|
server=ensure_str(tracker.get_name()),
|
||||||
shareids=sorted(tracker.buckets.keys()),
|
shareids=sorted(tracker.buckets.keys()),
|
||||||
)
|
)
|
||||||
for tracker
|
for tracker
|
||||||
|
@ -123,7 +123,7 @@ _UPLOAD_TRACKERS = Field(
|
||||||
|
|
||||||
_ALREADY_SERVERIDS = Field(
|
_ALREADY_SERVERIDS = Field(
|
||||||
u"already_serverids",
|
u"already_serverids",
|
||||||
lambda d: d,
|
lambda d: {str(k): v for k, v in d.items()},
|
||||||
u"Some servers which are already holding some shares that we were interested in uploading.",
|
u"Some servers which are already holding some shares that we were interested in uploading.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,6 @@ Ported to Python 3.
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from six import ensure_text
|
|
||||||
from typing import Union, Callable, Any, Optional, cast, Dict
|
from typing import Union, Callable, Any, Optional, cast, Dict
|
||||||
from os import urandom
|
from os import urandom
|
||||||
import re
|
import re
|
||||||
|
@ -273,7 +272,6 @@ class StorageFarmBroker(service.MultiService):
|
||||||
# doesn't really matter but it makes the logging behavior more
|
# doesn't really matter but it makes the logging behavior more
|
||||||
# predictable and easier to test (and at least one test does depend on
|
# predictable and easier to test (and at least one test does depend on
|
||||||
# this sorted order).
|
# this sorted order).
|
||||||
servers = {ensure_text(key): value for (key, value) in servers.items()}
|
|
||||||
for (server_id, server) in sorted(servers.items()):
|
for (server_id, server) in sorted(servers.items()):
|
||||||
try:
|
try:
|
||||||
storage_server = self._make_storage_server(
|
storage_server = self._make_storage_server(
|
||||||
|
|
|
@ -125,5 +125,5 @@ if sys.platform == "win32":
|
||||||
initialize()
|
initialize()
|
||||||
|
|
||||||
from eliot import to_file
|
from eliot import to_file
|
||||||
from allmydata.util.eliotutil import eliot_json_encoder
|
from allmydata.util.jsonbytes import AnyBytesJSONEncoder
|
||||||
to_file(open("eliot.log", "wb"), encoder=eliot_json_encoder)
|
to_file(open("eliot.log", "wb"), encoder=AnyBytesJSONEncoder)
|
||||||
|
|
|
@ -1352,6 +1352,26 @@ class _TestCaseMixin(object):
|
||||||
def assertRaises(self, *a, **kw):
|
def assertRaises(self, *a, **kw):
|
||||||
return self._dummyCase.assertRaises(*a, **kw)
|
return self._dummyCase.assertRaises(*a, **kw)
|
||||||
|
|
||||||
|
def failUnless(self, *args, **kwargs):
|
||||||
|
"""Backwards compatibility method."""
|
||||||
|
self.assertTrue(*args, **kwargs)
|
||||||
|
|
||||||
|
def failIf(self, *args, **kwargs):
|
||||||
|
"""Backwards compatibility method."""
|
||||||
|
self.assertFalse(*args, **kwargs)
|
||||||
|
|
||||||
|
def failIfEqual(self, *args, **kwargs):
|
||||||
|
"""Backwards compatibility method."""
|
||||||
|
self.assertNotEqual(*args, **kwargs)
|
||||||
|
|
||||||
|
def failUnlessEqual(self, *args, **kwargs):
|
||||||
|
"""Backwards compatibility method."""
|
||||||
|
self.assertEqual(*args, **kwargs)
|
||||||
|
|
||||||
|
def failUnlessReallyEqual(self, *args, **kwargs):
|
||||||
|
"""Backwards compatibility method."""
|
||||||
|
self.assertReallyEqual(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SyncTestCase(_TestCaseMixin, TestCase):
|
class SyncTestCase(_TestCaseMixin, TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -29,6 +29,7 @@ from eliot import (
|
||||||
ILogger,
|
ILogger,
|
||||||
)
|
)
|
||||||
from eliot.testing import (
|
from eliot.testing import (
|
||||||
|
MemoryLogger,
|
||||||
swap_logger,
|
swap_logger,
|
||||||
check_for_errors,
|
check_for_errors,
|
||||||
)
|
)
|
||||||
|
@ -37,8 +38,8 @@ from twisted.python.monkey import (
|
||||||
MonkeyPatcher,
|
MonkeyPatcher,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..util.eliotutil import (
|
from ..util.jsonbytes import (
|
||||||
MemoryLogger,
|
AnyBytesJSONEncoder
|
||||||
)
|
)
|
||||||
|
|
||||||
_NAME = Field.for_types(
|
_NAME = Field.for_types(
|
||||||
|
@ -146,7 +147,7 @@ def with_logging(
|
||||||
"""
|
"""
|
||||||
@wraps(test_method)
|
@wraps(test_method)
|
||||||
def run_with_logging(*args, **kwargs):
|
def run_with_logging(*args, **kwargs):
|
||||||
validating_logger = MemoryLogger()
|
validating_logger = MemoryLogger(encoder=AnyBytesJSONEncoder)
|
||||||
original = swap_logger(None)
|
original = swap_logger(None)
|
||||||
try:
|
try:
|
||||||
swap_logger(_TwoLoggers(original, validating_logger))
|
swap_logger(_TwoLoggers(original, validating_logger))
|
||||||
|
|
|
@ -850,6 +850,7 @@ class StorageClients(SyncTestCase):
|
||||||
actionType=u"storage-client:broker:set-static-servers",
|
actionType=u"storage-client:broker:set-static-servers",
|
||||||
succeeded=True,
|
succeeded=True,
|
||||||
),
|
),
|
||||||
|
encoder_=json.AnyBytesJSONEncoder
|
||||||
)
|
)
|
||||||
def test_static_servers(self, logger):
|
def test_static_servers(self, logger):
|
||||||
"""
|
"""
|
||||||
|
@ -884,6 +885,7 @@ class StorageClients(SyncTestCase):
|
||||||
actionType=u"storage-client:broker:make-storage-server",
|
actionType=u"storage-client:broker:make-storage-server",
|
||||||
succeeded=False,
|
succeeded=False,
|
||||||
),
|
),
|
||||||
|
encoder_=json.AnyBytesJSONEncoder
|
||||||
)
|
)
|
||||||
def test_invalid_static_server(self, logger):
|
def test_invalid_static_server(self, logger):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -507,7 +507,7 @@ class TestUtil(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
remove a simple prefix properly
|
remove a simple prefix properly
|
||||||
"""
|
"""
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
remove_prefix(b"foobar", b"foo"),
|
remove_prefix(b"foobar", b"foo"),
|
||||||
b"bar"
|
b"bar"
|
||||||
)
|
)
|
||||||
|
@ -523,7 +523,7 @@ class TestUtil(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
removing a zero-length prefix does nothing
|
removing a zero-length prefix does nothing
|
||||||
"""
|
"""
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
remove_prefix(b"foobar", b""),
|
remove_prefix(b"foobar", b""),
|
||||||
b"foobar",
|
b"foobar",
|
||||||
)
|
)
|
||||||
|
@ -532,7 +532,7 @@ class TestUtil(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
removing a prefix which is the whole string is empty
|
removing a prefix which is the whole string is empty
|
||||||
"""
|
"""
|
||||||
self.assertEquals(
|
self.assertEqual(
|
||||||
remove_prefix(b"foobar", b"foobar"),
|
remove_prefix(b"foobar", b"foobar"),
|
||||||
b"",
|
b"",
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,7 +47,6 @@ from eliot import (
|
||||||
Message,
|
Message,
|
||||||
MessageType,
|
MessageType,
|
||||||
fields,
|
fields,
|
||||||
FileDestination,
|
|
||||||
MemoryLogger,
|
MemoryLogger,
|
||||||
)
|
)
|
||||||
from eliot.twisted import DeferredContext
|
from eliot.twisted import DeferredContext
|
||||||
|
@ -64,7 +63,6 @@ from twisted.internet.task import deferLater
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
|
|
||||||
from ..util.eliotutil import (
|
from ..util.eliotutil import (
|
||||||
eliot_json_encoder,
|
|
||||||
log_call_deferred,
|
log_call_deferred,
|
||||||
_parse_destination_description,
|
_parse_destination_description,
|
||||||
_EliotLogging,
|
_EliotLogging,
|
||||||
|
@ -188,8 +186,8 @@ class ParseDestinationDescriptionTests(SyncTestCase):
|
||||||
"""
|
"""
|
||||||
reactor = object()
|
reactor = object()
|
||||||
self.assertThat(
|
self.assertThat(
|
||||||
_parse_destination_description("file:-")(reactor),
|
_parse_destination_description("file:-")(reactor).file,
|
||||||
Equals(FileDestination(stdout, encoder=eliot_json_encoder)),
|
Equals(stdout),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,14 @@ INTRODUCERS_CFG_FURLS_COMMENTED="""introducers:
|
||||||
|
|
||||||
class MultiIntroTests(unittest.TestCase):
|
class MultiIntroTests(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
async def setUp(self):
|
||||||
# setup tahoe.cfg and basedir/private/introducers
|
# setup tahoe.cfg and basedir/private/introducers
|
||||||
# create a custom tahoe.cfg
|
# create a custom tahoe.cfg
|
||||||
self.basedir = os.path.dirname(self.mktemp())
|
self.basedir = os.path.dirname(self.mktemp())
|
||||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||||
config = {'hide-ip':False, 'listen': 'tcp',
|
config = {'hide-ip':False, 'listen': 'tcp',
|
||||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||||
write_node_config(c, config)
|
await write_node_config(c, config)
|
||||||
c.write("[storage]\n")
|
c.write("[storage]\n")
|
||||||
c.write("enabled = false\n")
|
c.write("enabled = false\n")
|
||||||
c.close()
|
c.close()
|
||||||
|
@ -63,8 +63,7 @@ class MultiIntroTests(unittest.TestCase):
|
||||||
# assertions
|
# assertions
|
||||||
self.failUnlessEqual(ic_count, len(connections["introducers"]))
|
self.failUnlessEqual(ic_count, len(connections["introducers"]))
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
async def test_read_introducer_furl_from_tahoecfg(self):
|
||||||
def test_read_introducer_furl_from_tahoecfg(self):
|
|
||||||
"""
|
"""
|
||||||
The deprecated [client]introducer.furl item is still read and respected.
|
The deprecated [client]introducer.furl item is still read and respected.
|
||||||
"""
|
"""
|
||||||
|
@ -72,7 +71,7 @@ class MultiIntroTests(unittest.TestCase):
|
||||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||||
config = {'hide-ip':False, 'listen': 'tcp',
|
config = {'hide-ip':False, 'listen': 'tcp',
|
||||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||||
write_node_config(c, config)
|
await write_node_config(c, config)
|
||||||
fake_furl = "furl1"
|
fake_furl = "furl1"
|
||||||
c.write("[client]\n")
|
c.write("[client]\n")
|
||||||
c.write("introducer.furl = %s\n" % fake_furl)
|
c.write("introducer.furl = %s\n" % fake_furl)
|
||||||
|
@ -139,14 +138,14 @@ introducers:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class NoDefault(unittest.TestCase):
|
class NoDefault(unittest.TestCase):
|
||||||
def setUp(self):
|
async def setUp(self):
|
||||||
# setup tahoe.cfg and basedir/private/introducers
|
# setup tahoe.cfg and basedir/private/introducers
|
||||||
# create a custom tahoe.cfg
|
# create a custom tahoe.cfg
|
||||||
self.basedir = os.path.dirname(self.mktemp())
|
self.basedir = os.path.dirname(self.mktemp())
|
||||||
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
c = open(os.path.join(self.basedir, "tahoe.cfg"), "w")
|
||||||
config = {'hide-ip':False, 'listen': 'tcp',
|
config = {'hide-ip':False, 'listen': 'tcp',
|
||||||
'port': None, 'location': None, 'hostname': 'example.net'}
|
'port': None, 'location': None, 'hostname': 'example.net'}
|
||||||
write_node_config(c, config)
|
await write_node_config(c, config)
|
||||||
c.write("[storage]\n")
|
c.write("[storage]\n")
|
||||||
c.write("enabled = false\n")
|
c.write("enabled = false\n")
|
||||||
c.close()
|
c.close()
|
||||||
|
|
|
@ -26,7 +26,7 @@ from typing import Union, Callable, Tuple, Iterable
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from cbor2 import dumps
|
from cbor2 import dumps
|
||||||
from pycddl import ValidationError as CDDLValidationError
|
from pycddl import ValidationError as CDDLValidationError
|
||||||
from hypothesis import assume, given, strategies as st
|
from hypothesis import assume, given, strategies as st, settings as hypothesis_settings
|
||||||
from fixtures import Fixture, TempDir, MonkeyPatch
|
from fixtures import Fixture, TempDir, MonkeyPatch
|
||||||
from treq.testing import StubTreq
|
from treq.testing import StubTreq
|
||||||
from klein import Klein
|
from klein import Klein
|
||||||
|
@ -442,6 +442,9 @@ class CustomHTTPServerTests(SyncTestCase):
|
||||||
result_of(client.get_version())
|
result_of(client.get_version())
|
||||||
|
|
||||||
@given(length=st.integers(min_value=1, max_value=1_000_000))
|
@given(length=st.integers(min_value=1, max_value=1_000_000))
|
||||||
|
# On Python 3.12 we're getting weird deadline issues in CI, so disabling
|
||||||
|
# for now.
|
||||||
|
@hypothesis_settings(deadline=None)
|
||||||
def test_limited_content_fits(self, length):
|
def test_limited_content_fits(self, length):
|
||||||
"""
|
"""
|
||||||
``http_client.limited_content()`` returns the body if it is less than
|
``http_client.limited_content()`` returns the body if it is less than
|
||||||
|
|
|
@ -23,7 +23,7 @@ def assert_soup_has_favicon(testcase, soup):
|
||||||
``BeautifulSoup`` object ``soup`` contains the tahoe favicon link.
|
``BeautifulSoup`` object ``soup`` contains the tahoe favicon link.
|
||||||
"""
|
"""
|
||||||
links = soup.find_all(u'link', rel=u'shortcut icon')
|
links = soup.find_all(u'link', rel=u'shortcut icon')
|
||||||
testcase.assert_(
|
testcase.assertTrue(
|
||||||
any(t[u'href'] == u'/icon.png' for t in links), soup)
|
any(t[u'href'] == u'/icon.png' for t in links), soup)
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,6 +92,6 @@ def assert_soup_has_text(testcase, soup, text):
|
||||||
``BeautifulSoup`` object ``soup`` contains the passed in ``text`` anywhere
|
``BeautifulSoup`` object ``soup`` contains the passed in ``text`` anywhere
|
||||||
as a text node.
|
as a text node.
|
||||||
"""
|
"""
|
||||||
testcase.assert_(
|
testcase.assertTrue(
|
||||||
soup.find_all(string=re.compile(re.escape(text))),
|
soup.find_all(string=re.compile(re.escape(text))),
|
||||||
soup)
|
soup)
|
||||||
|
|
|
@ -117,7 +117,7 @@ class TestStreamingLogs(AsyncTestCase):
|
||||||
proto.transport.loseConnection()
|
proto.transport.loseConnection()
|
||||||
yield proto.is_closed
|
yield proto.is_closed
|
||||||
|
|
||||||
self.assertThat(len(messages), Equals(3))
|
self.assertThat(len(messages), Equals(3), messages)
|
||||||
self.assertThat(messages[0]["action_type"], Equals("test:cli:some-exciting-action"))
|
self.assertThat(messages[0]["action_type"], Equals("test:cli:some-exciting-action"))
|
||||||
self.assertThat(messages[0]["arguments"],
|
self.assertThat(messages[0]["arguments"],
|
||||||
Equals(["hello", "good-\\xff-day", 123, {"a": 35}, [None]]))
|
Equals(["hello", "good-\\xff-day", 123, {"a": 35}, [None]]))
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
"""
|
|
||||||
Bring in some Eliot updates from newer versions of Eliot than we can
|
|
||||||
depend on in Python 2. The implementations are copied from Eliot 1.14 and
|
|
||||||
only changed enough to add Python 2 compatibility.
|
|
||||||
|
|
||||||
Every API in this module (except ``eliot_json_encoder``) should be obsolete as
|
|
||||||
soon as we depend on Eliot 1.14 or newer.
|
|
||||||
|
|
||||||
When that happens:
|
|
||||||
|
|
||||||
* replace ``capture_logging``
|
|
||||||
with ``partial(eliot.testing.capture_logging, encoder_=eliot_json_encoder)``
|
|
||||||
* replace ``validateLogging``
|
|
||||||
with ``partial(eliot.testing.validateLogging, encoder_=eliot_json_encoder)``
|
|
||||||
* replace ``MemoryLogger``
|
|
||||||
with ``partial(eliot.MemoryLogger, encoder=eliot_json_encoder)``
|
|
||||||
|
|
||||||
Ported to Python 3.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from future.utils import PY2
|
|
||||||
if PY2:
|
|
||||||
from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
|
||||||
|
|
||||||
import json as pyjson
|
|
||||||
from functools import wraps, partial
|
|
||||||
|
|
||||||
from eliot import (
|
|
||||||
MemoryLogger as _MemoryLogger,
|
|
||||||
)
|
|
||||||
|
|
||||||
from eliot.testing import (
|
|
||||||
check_for_errors,
|
|
||||||
swap_logger,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .jsonbytes import AnyBytesJSONEncoder
|
|
||||||
|
|
||||||
# There are currently a number of log messages that include non-UTF-8 bytes.
|
|
||||||
# Allow these, at least for now. Later when the whole test suite has been
|
|
||||||
# converted to our SyncTestCase or AsyncTestCase it will be easier to turn
|
|
||||||
# this off and then attribute log failures to specific codepaths so they can
|
|
||||||
# be fixed (and then not regressed later) because those instances will result
|
|
||||||
# in test failures instead of only garbage being written to the eliot log.
|
|
||||||
eliot_json_encoder = AnyBytesJSONEncoder
|
|
||||||
|
|
||||||
class _CustomEncoderMemoryLogger(_MemoryLogger):
|
|
||||||
"""
|
|
||||||
Override message validation from the Eliot-supplied ``MemoryLogger`` to
|
|
||||||
use our chosen JSON encoder.
|
|
||||||
|
|
||||||
This is only necessary on Python 2 where we use an old version of Eliot
|
|
||||||
that does not parameterize the encoder.
|
|
||||||
"""
|
|
||||||
def __init__(self, encoder=eliot_json_encoder):
|
|
||||||
"""
|
|
||||||
@param encoder: A JSONEncoder subclass to use when encoding JSON.
|
|
||||||
"""
|
|
||||||
self._encoder = encoder
|
|
||||||
super(_CustomEncoderMemoryLogger, self).__init__()
|
|
||||||
|
|
||||||
def _validate_message(self, dictionary, serializer):
|
|
||||||
"""Validate an individual message.
|
|
||||||
|
|
||||||
As a side-effect, the message is replaced with its serialized contents.
|
|
||||||
|
|
||||||
@param dictionary: A message C{dict} to be validated. Might be mutated
|
|
||||||
by the serializer!
|
|
||||||
|
|
||||||
@param serializer: C{None} or a serializer.
|
|
||||||
|
|
||||||
@raises TypeError: If a field name is not unicode, or the dictionary
|
|
||||||
fails to serialize to JSON.
|
|
||||||
|
|
||||||
@raises eliot.ValidationError: If serializer was given and validation
|
|
||||||
failed.
|
|
||||||
"""
|
|
||||||
if serializer is not None:
|
|
||||||
serializer.validate(dictionary)
|
|
||||||
for key in dictionary:
|
|
||||||
if not isinstance(key, str):
|
|
||||||
if isinstance(key, bytes):
|
|
||||||
key.decode("utf-8")
|
|
||||||
else:
|
|
||||||
raise TypeError(dictionary, "%r is not unicode" % (key,))
|
|
||||||
if serializer is not None:
|
|
||||||
serializer.serialize(dictionary)
|
|
||||||
|
|
||||||
try:
|
|
||||||
pyjson.dumps(dictionary, cls=self._encoder)
|
|
||||||
except Exception as e:
|
|
||||||
raise TypeError("Message %s doesn't encode to JSON: %s" % (dictionary, e))
|
|
||||||
|
|
||||||
if PY2:
|
|
||||||
MemoryLogger = partial(_CustomEncoderMemoryLogger, encoder=eliot_json_encoder)
|
|
||||||
else:
|
|
||||||
MemoryLogger = partial(_MemoryLogger, encoder=eliot_json_encoder)
|
|
||||||
|
|
||||||
def validateLogging(
|
|
||||||
assertion, *assertionArgs, **assertionKwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Decorator factory for L{unittest.TestCase} methods to add logging
|
|
||||||
validation.
|
|
||||||
|
|
||||||
1. The decorated test method gets a C{logger} keyword argument, a
|
|
||||||
L{MemoryLogger}.
|
|
||||||
2. All messages logged to this logger will be validated at the end of
|
|
||||||
the test.
|
|
||||||
3. Any unflushed logged tracebacks will cause the test to fail.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
from unittest import TestCase
|
|
||||||
from eliot.testing import assertContainsFields, validateLogging
|
|
||||||
|
|
||||||
class MyTests(TestCase):
|
|
||||||
def assertFooLogging(self, logger):
|
|
||||||
assertContainsFields(self, logger.messages[0], {"key": 123})
|
|
||||||
|
|
||||||
|
|
||||||
@param assertion: A callable that will be called with the
|
|
||||||
L{unittest.TestCase} instance, the logger and C{assertionArgs} and
|
|
||||||
C{assertionKwargs} once the actual test has run, allowing for extra
|
|
||||||
logging-related assertions on the effects of the test. Use L{None} if you
|
|
||||||
want the cleanup assertions registered but no custom assertions.
|
|
||||||
|
|
||||||
@param assertionArgs: Additional positional arguments to pass to
|
|
||||||
C{assertion}.
|
|
||||||
|
|
||||||
@param assertionKwargs: Additional keyword arguments to pass to
|
|
||||||
C{assertion}.
|
|
||||||
|
|
||||||
@param encoder_: C{json.JSONEncoder} subclass to use when validating JSON.
|
|
||||||
"""
|
|
||||||
encoder_ = assertionKwargs.pop("encoder_", eliot_json_encoder)
|
|
||||||
def decorator(function):
|
|
||||||
@wraps(function)
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
skipped = False
|
|
||||||
|
|
||||||
kwargs["logger"] = logger = MemoryLogger(encoder=encoder_)
|
|
||||||
self.addCleanup(check_for_errors, logger)
|
|
||||||
# TestCase runs cleanups in reverse order, and we want this to
|
|
||||||
# run *before* tracebacks are checked:
|
|
||||||
if assertion is not None:
|
|
||||||
self.addCleanup(
|
|
||||||
lambda: skipped
|
|
||||||
or assertion(self, logger, *assertionArgs, **assertionKwargs)
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
return function(self, *args, **kwargs)
|
|
||||||
except self.skipException:
|
|
||||||
skipped = True
|
|
||||||
raise
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
# PEP 8 variant:
|
|
||||||
validate_logging = validateLogging
|
|
||||||
|
|
||||||
def capture_logging(
|
|
||||||
assertion, *assertionArgs, **assertionKwargs
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Capture and validate all logging that doesn't specify a L{Logger}.
|
|
||||||
|
|
||||||
See L{validate_logging} for details on the rest of its behavior.
|
|
||||||
"""
|
|
||||||
encoder_ = assertionKwargs.pop("encoder_", eliot_json_encoder)
|
|
||||||
def decorator(function):
|
|
||||||
@validate_logging(
|
|
||||||
assertion, *assertionArgs, encoder_=encoder_, **assertionKwargs
|
|
||||||
)
|
|
||||||
@wraps(function)
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
logger = kwargs["logger"]
|
|
||||||
previous_logger = swap_logger(logger)
|
|
||||||
|
|
||||||
def cleanup():
|
|
||||||
swap_logger(previous_logger)
|
|
||||||
|
|
||||||
self.addCleanup(cleanup)
|
|
||||||
return function(self, *args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
return decorator
|
|
|
@ -3,17 +3,6 @@ Tools aimed at the interaction between Tahoe-LAFS implementation and Eliot.
|
||||||
|
|
||||||
Ported to Python 3.
|
Ported to Python 3.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from __future__ import (
|
|
||||||
unicode_literals,
|
|
||||||
print_function,
|
|
||||||
absolute_import,
|
|
||||||
division,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"MemoryLogger",
|
"MemoryLogger",
|
||||||
|
@ -26,11 +15,6 @@ __all__ = [
|
||||||
"capture_logging",
|
"capture_logging",
|
||||||
]
|
]
|
||||||
|
|
||||||
from future.utils import PY2
|
|
||||||
if PY2:
|
|
||||||
from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
|
||||||
from six import ensure_text
|
|
||||||
|
|
||||||
from sys import (
|
from sys import (
|
||||||
stdout,
|
stdout,
|
||||||
)
|
)
|
||||||
|
@ -42,6 +26,7 @@ from logging import (
|
||||||
)
|
)
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
|
from six import ensure_text
|
||||||
from zope.interface import (
|
from zope.interface import (
|
||||||
implementer,
|
implementer,
|
||||||
)
|
)
|
||||||
|
@ -61,6 +46,11 @@ from eliot import (
|
||||||
write_traceback,
|
write_traceback,
|
||||||
start_action,
|
start_action,
|
||||||
)
|
)
|
||||||
|
from eliot.testing import (
|
||||||
|
MemoryLogger,
|
||||||
|
capture_logging,
|
||||||
|
)
|
||||||
|
|
||||||
from eliot._validation import (
|
from eliot._validation import (
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
|
@ -87,11 +77,8 @@ from twisted.internet.defer import (
|
||||||
)
|
)
|
||||||
from twisted.application.service import Service
|
from twisted.application.service import Service
|
||||||
|
|
||||||
from ._eliot_updates import (
|
from .jsonbytes import AnyBytesJSONEncoder
|
||||||
MemoryLogger,
|
|
||||||
eliot_json_encoder,
|
|
||||||
capture_logging,
|
|
||||||
)
|
|
||||||
|
|
||||||
def validateInstanceOf(t):
|
def validateInstanceOf(t):
|
||||||
"""
|
"""
|
||||||
|
@ -309,7 +296,7 @@ class _DestinationParser(object):
|
||||||
rotateLength=rotate_length,
|
rotateLength=rotate_length,
|
||||||
maxRotatedFiles=max_rotated_files,
|
maxRotatedFiles=max_rotated_files,
|
||||||
)
|
)
|
||||||
return lambda reactor: FileDestination(get_file(), eliot_json_encoder)
|
return lambda reactor: FileDestination(get_file(), encoder=AnyBytesJSONEncoder)
|
||||||
|
|
||||||
|
|
||||||
_parse_destination_description = _DestinationParser().parse
|
_parse_destination_description = _DestinationParser().parse
|
||||||
|
|
|
@ -61,6 +61,9 @@ class UTF8BytesJSONEncoder(json.JSONEncoder):
|
||||||
"""
|
"""
|
||||||
A JSON encoder than can also encode UTF-8 encoded strings.
|
A JSON encoder than can also encode UTF-8 encoded strings.
|
||||||
"""
|
"""
|
||||||
|
def default(self, o):
|
||||||
|
return bytes_to_unicode(False, o)
|
||||||
|
|
||||||
def encode(self, o, **kwargs):
|
def encode(self, o, **kwargs):
|
||||||
return json.JSONEncoder.encode(
|
return json.JSONEncoder.encode(
|
||||||
self, bytes_to_unicode(False, o), **kwargs)
|
self, bytes_to_unicode(False, o), **kwargs)
|
||||||
|
@ -77,6 +80,9 @@ class AnyBytesJSONEncoder(json.JSONEncoder):
|
||||||
Bytes are decoded to strings using UTF-8, if that fails to decode then the
|
Bytes are decoded to strings using UTF-8, if that fails to decode then the
|
||||||
bytes are quoted.
|
bytes are quoted.
|
||||||
"""
|
"""
|
||||||
|
def default(self, o):
|
||||||
|
return bytes_to_unicode(True, o)
|
||||||
|
|
||||||
def encode(self, o, **kwargs):
|
def encode(self, o, **kwargs):
|
||||||
return json.JSONEncoder.encode(
|
return json.JSONEncoder.encode(
|
||||||
self, bytes_to_unicode(True, o), **kwargs)
|
self, bytes_to_unicode(True, o), **kwargs)
|
||||||
|
|
20
tox.ini
20
tox.ini
|
@ -11,6 +11,7 @@ python =
|
||||||
3.9: py39-coverage
|
3.9: py39-coverage
|
||||||
3.10: py310-coverage
|
3.10: py310-coverage
|
||||||
3.11: py311-coverage
|
3.11: py311-coverage
|
||||||
|
3.12: py312-coverage
|
||||||
pypy-3.8: pypy38
|
pypy-3.8: pypy38
|
||||||
pypy-3.9: pypy39
|
pypy-3.9: pypy39
|
||||||
|
|
||||||
|
@ -18,11 +19,14 @@ python =
|
||||||
twisted = 1
|
twisted = 1
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = typechecks,codechecks,py{38,39,310,311}-{coverage},pypy27,pypy38,pypy39,integration
|
envlist = typechecks,codechecks,py{38,39,310,311,312}-{coverage},pypy27,pypy38,pypy39,integration
|
||||||
minversion = 2.4
|
minversion = 4
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
passenv = TAHOE_LAFS_* PIP_* SUBUNITREPORTER_* USERPROFILE HOMEDRIVE HOMEPATH
|
# Install code the real way, for maximum realism.
|
||||||
|
usedevelop = False
|
||||||
|
|
||||||
|
passenv = TAHOE_LAFS_*,PIP_*,SUBUNITREPORTER_*,USERPROFILE,HOMEDRIVE,HOMEPATH,COLUMNS
|
||||||
deps =
|
deps =
|
||||||
# We pull in certify *here* to avoid bug #2913. Basically if a
|
# We pull in certify *here* to avoid bug #2913. Basically if a
|
||||||
# `setup_requires=...` causes a package to be installed (with setuptools)
|
# `setup_requires=...` causes a package to be installed (with setuptools)
|
||||||
|
@ -40,10 +44,6 @@ deps =
|
||||||
# with the above pins.
|
# with the above pins.
|
||||||
certifi
|
certifi
|
||||||
|
|
||||||
# We add usedevelop=False because testing against a true installation gives
|
|
||||||
# more useful results.
|
|
||||||
usedevelop = False
|
|
||||||
|
|
||||||
extras =
|
extras =
|
||||||
# Get general testing environment dependencies so we can run the tests
|
# Get general testing environment dependencies so we can run the tests
|
||||||
# how we like.
|
# how we like.
|
||||||
|
@ -56,6 +56,7 @@ setenv =
|
||||||
# Define TEST_SUITE in the environment as an aid to constructing the
|
# Define TEST_SUITE in the environment as an aid to constructing the
|
||||||
# correct test command below.
|
# correct test command below.
|
||||||
TEST_SUITE = allmydata
|
TEST_SUITE = allmydata
|
||||||
|
COLUMNS = 80
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
# As an aid to debugging, dump all of the Python packages and their
|
# As an aid to debugging, dump all of the Python packages and their
|
||||||
|
@ -81,6 +82,7 @@ commands =
|
||||||
coverage: coverage xml
|
coverage: coverage xml
|
||||||
|
|
||||||
[testenv:integration]
|
[testenv:integration]
|
||||||
|
usedevelop = False
|
||||||
basepython = python3
|
basepython = python3
|
||||||
platform = mylinux: linux
|
platform = mylinux: linux
|
||||||
mymacos: darwin
|
mymacos: darwin
|
||||||
|
@ -140,7 +142,7 @@ commands = mypy src
|
||||||
|
|
||||||
|
|
||||||
[testenv:draftnews]
|
[testenv:draftnews]
|
||||||
passenv = TAHOE_LAFS_* PIP_* SUBUNITREPORTER_* USERPROFILE HOMEDRIVE HOMEPATH
|
passenv = TAHOE_LAFS_*,PIP_*,SUBUNITREPORTER_*,USERPROFILE,HOMEDRIVE,HOMEPATH,COLUMNS
|
||||||
deps =
|
deps =
|
||||||
# see comment in [testenv] about "certifi"
|
# see comment in [testenv] about "certifi"
|
||||||
certifi
|
certifi
|
||||||
|
@ -150,7 +152,7 @@ commands =
|
||||||
|
|
||||||
[testenv:news]
|
[testenv:news]
|
||||||
# On macOS, git invoked from Tox needs $HOME.
|
# On macOS, git invoked from Tox needs $HOME.
|
||||||
passenv = TAHOE_LAFS_* PIP_* SUBUNITREPORTER_* USERPROFILE HOMEDRIVE HOMEPATH HOME
|
passenv = TAHOE_LAFS_*,PIP_*,SUBUNITREPORTER_*,USERPROFILE,HOMEDRIVE,HOMEPATH,COLUMNS
|
||||||
whitelist_externals =
|
whitelist_externals =
|
||||||
git
|
git
|
||||||
deps =
|
deps =
|
||||||
|
|
Loading…
Reference in New Issue