Bump hacking
hacking 3.1.x is too old. Change-Id: Ic5131276ac1d1a1a959d0a5b16398ae12fae0c18
This commit is contained in:
parent
5775bcef47
commit
b488f42332
@ -32,7 +32,7 @@ repos:
|
|||||||
- id: flake8
|
- id: flake8
|
||||||
name: flake8
|
name: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- hacking>=3.1.0,<3.2.0
|
- hacking>=6.1.0,<6.2.0
|
||||||
language: python
|
language: python
|
||||||
entry: flake8
|
entry: flake8
|
||||||
files: '^.*\.py$'
|
files: '^.*\.py$'
|
||||||
|
@ -219,7 +219,7 @@ def main():
|
|||||||
|
|
||||||
# Run consumer
|
# Run consumer
|
||||||
print_with_time("Consumer was successfully run.")
|
print_with_time("Consumer was successfully run.")
|
||||||
while(True):
|
while True:
|
||||||
messages = pop_zaqar_messages(
|
messages = pop_zaqar_messages(
|
||||||
zaqarclientwrapper.ZAQARCLIENT, CONF.zaqar.zaqar_queues)
|
zaqarclientwrapper.ZAQARCLIENT, CONF.zaqar.zaqar_queues)
|
||||||
if not messages:
|
if not messages:
|
||||||
|
@ -267,7 +267,7 @@ class APIVersionRequest(utils.ComparableMixin):
|
|||||||
|
|
||||||
@experimental.setter
|
@experimental.setter
|
||||||
def experimental(self, value):
|
def experimental(self, value):
|
||||||
if type(value) != bool:
|
if not isinstance(value, bool):
|
||||||
msg = _('The experimental property must be a bool value.')
|
msg = _('The experimental property must be a bool value.')
|
||||||
raise exception.InvalidParameterValue(err=msg)
|
raise exception.InvalidParameterValue(err=msg)
|
||||||
self._experimental = value
|
self._experimental = value
|
||||||
@ -275,7 +275,7 @@ class APIVersionRequest(utils.ComparableMixin):
|
|||||||
def matches_versioned_method(self, method):
|
def matches_versioned_method(self, method):
|
||||||
"""Compares this version to that of a versioned method."""
|
"""Compares this version to that of a versioned method."""
|
||||||
|
|
||||||
if type(method) != versioned_method.VersionedMethod:
|
if not isinstance(method, versioned_method.VersionedMethod):
|
||||||
msg = _('An API version request must be compared '
|
msg = _('An API version request must be compared '
|
||||||
'to a VersionedMethod object.')
|
'to a VersionedMethod object.')
|
||||||
raise exception.InvalidParameterValue(err=msg)
|
raise exception.InvalidParameterValue(err=msg)
|
||||||
|
@ -56,7 +56,7 @@ class ShareSnapshotMixin(object):
|
|||||||
snapshot = self.share_api.get_snapshot(context, id)
|
snapshot = self.share_api.get_snapshot(context, id)
|
||||||
|
|
||||||
# Snapshot with no instances is filtered out.
|
# Snapshot with no instances is filtered out.
|
||||||
if(snapshot.get('status') is None):
|
if snapshot.get('status') is None:
|
||||||
raise exc.HTTPNotFound()
|
raise exc.HTTPNotFound()
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
raise exc.HTTPNotFound()
|
raise exc.HTTPNotFound()
|
||||||
|
@ -142,11 +142,11 @@ class ShareNetworkController(wsgi.Controller, wsgi.AdminActionsMixin):
|
|||||||
id,
|
id,
|
||||||
security_service['id'])
|
security_service['id'])
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(
|
msg = ("Failed to delete security association of network "
|
||||||
"Failed to delete security association of network "
|
|
||||||
"{net_id} and security service "
|
"{net_id} and security service "
|
||||||
"{sec_id}".format(net_id=id,
|
"{sec_id}".format(net_id=id,
|
||||||
sec_id=security_service['id']))
|
sec_id=security_service['id']))
|
||||||
|
LOG.exception(msg)
|
||||||
|
|
||||||
db_api.share_network_delete(context, id)
|
db_api.share_network_delete(context, id)
|
||||||
|
|
||||||
|
@ -352,11 +352,11 @@ class IpRouteCommand(IpDeviceCommandBase):
|
|||||||
'list', 'proto', 'kernel', 'exact', subnet).split('\n')
|
'list', 'proto', 'kernel', 'exact', subnet).split('\n')
|
||||||
for subnet_route_line in subnet_route_list_lines:
|
for subnet_route_line in subnet_route_list_lines:
|
||||||
i = iter(subnet_route_line.split())
|
i = iter(subnet_route_line.split())
|
||||||
while(next(i) != 'dev'):
|
while next(i) != 'dev':
|
||||||
pass
|
pass
|
||||||
device = next(i)
|
device = next(i)
|
||||||
try:
|
try:
|
||||||
while(next(i) != 'src'):
|
while next(i) != 'src':
|
||||||
pass
|
pass
|
||||||
src = next(i)
|
src = next(i)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -587,8 +587,8 @@ class CephFSDriver(driver.ExecuteMixin, driver.GaneshaMixin,
|
|||||||
if share['share_group_id'] is not None:
|
if share['share_group_id'] is not None:
|
||||||
argdict.update({"group_name": share["share_group_id"]})
|
argdict.update({"group_name": share["share_group_id"]})
|
||||||
|
|
||||||
LOG.debug("extend_share {id} {size}".format(
|
LOG.debug("extend_share {id} {size}",
|
||||||
id=share['id'], size=new_size))
|
{"id": share['id'], "size": new_size})
|
||||||
|
|
||||||
rados_command(self.rados_client, "fs subvolume resize", argdict)
|
rados_command(self.rados_client, "fs subvolume resize", argdict)
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@ class GaneshaManager(object):
|
|||||||
try:
|
try:
|
||||||
self.execute('mv', tmpf, path)
|
self.execute('mv', tmpf, path)
|
||||||
except exception.ProcessExecutionError as e:
|
except exception.ProcessExecutionError as e:
|
||||||
LOG.error('mv temp file ({0}) to {1} failed.'.format(tmpf, path))
|
LOG.error('mv temp file (%s) to %s failed.', tmpf, path)
|
||||||
self.execute('rm', tmpf)
|
self.execute('rm', tmpf)
|
||||||
raise exception.GaneshaCommandFailure(
|
raise exception.GaneshaCommandFailure(
|
||||||
stdout=e.stdout, stderr=e.stderr, exit_code=e.exit_code,
|
stdout=e.stdout, stderr=e.stderr, exit_code=e.exit_code,
|
||||||
@ -546,8 +546,8 @@ class GaneshaManager(object):
|
|||||||
bytes_read = ioctx.read(object_name, max_size)
|
bytes_read = ioctx.read(object_name, max_size)
|
||||||
if ((len(bytes_read) == max_size) and
|
if ((len(bytes_read) == max_size) and
|
||||||
(ioctx.read(object_name, 1, offset=max_size))):
|
(ioctx.read(object_name, 1, offset=max_size))):
|
||||||
LOG.warning("Size of object {0} exceeds '{1}' bytes "
|
LOG.warning("Size of object %s exceeds '%d' bytes "
|
||||||
"read".format(object_name, max_size))
|
"read", object_name, max_size)
|
||||||
finally:
|
finally:
|
||||||
ioctx.close()
|
ioctx.close()
|
||||||
|
|
||||||
@ -595,7 +595,7 @@ class GaneshaManager(object):
|
|||||||
try:
|
try:
|
||||||
ioctx.remove_object(object_name)
|
ioctx.remove_object(object_name)
|
||||||
except rados.ObjectNotFound:
|
except rados.ObjectNotFound:
|
||||||
LOG.warning("Object '{0}' was already removed".format(object_name))
|
LOG.warning("Object '%s' was already removed", object_name)
|
||||||
finally:
|
finally:
|
||||||
ioctx.close()
|
ioctx.close()
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ class GlusterfsShareDriverBase(driver.ShareDriver):
|
|||||||
False)
|
False)
|
||||||
|
|
||||||
def _setup_via_manager(self, share_mgr, share_mgr_parent=None):
|
def _setup_via_manager(self, share_mgr, share_mgr_parent=None):
|
||||||
"""Callback for layout's `create_share` and `create_share_from_snapshot`
|
"""Callback for layout's `create_share`/`create_share_from_snapshot`
|
||||||
|
|
||||||
:param share_mgr: a {'share': <share>, 'manager': <gmgr>}
|
:param share_mgr: a {'share': <share>, 'manager': <gmgr>}
|
||||||
dict where <share> is the share created
|
dict where <share> is the share created
|
||||||
|
@ -2690,8 +2690,8 @@ class ShareManager(manager.SchedulerDependentManager):
|
|||||||
|
|
||||||
updated_export_locs = updated_replica.get(
|
updated_export_locs = updated_replica.get(
|
||||||
'export_locations')
|
'export_locations')
|
||||||
if(updated_export_locs is not None
|
if updated_export_locs is not None \
|
||||||
and isinstance(updated_export_locs, list)):
|
and isinstance(updated_export_locs, list):
|
||||||
self.db.export_locations_update(
|
self.db.export_locations_update(
|
||||||
context, updated_replica['id'],
|
context, updated_replica['id'],
|
||||||
updated_export_locs)
|
updated_export_locs)
|
||||||
|
@ -406,7 +406,7 @@ class TestCase(base_test.BaseTestCase):
|
|||||||
try:
|
try:
|
||||||
f = super(TestCase, self).assertIn
|
f = super(TestCase, self).assertIn
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.assertTrue(a in b, *args, **kwargs)
|
self.assertIn(a, b, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
f(a, b, *args, **kwargs)
|
f(a, b, *args, **kwargs)
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ class TestCase(base_test.BaseTestCase):
|
|||||||
try:
|
try:
|
||||||
f = super(TestCase, self).assertNotIn
|
f = super(TestCase, self).assertNotIn
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.assertFalse(a in b, *args, **kwargs)
|
self.assertNotIn(a, b, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
f(a, b, *args, **kwargs)
|
f(a, b, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ class ShareAccessDatabaseAPITestCase(test.TestCase):
|
|||||||
|
|
||||||
share_access_keys_present = True if with_share_access_data else False
|
share_access_keys_present = True if with_share_access_data else False
|
||||||
actual_access_ids = [r['access_id'] for r in rules]
|
actual_access_ids = [r['access_id'] for r in rules]
|
||||||
self.assertTrue(isinstance(actual_access_ids, list))
|
self.assertIsInstance(actual_access_ids, list)
|
||||||
expected = [access_1['id'], access_2['id']]
|
expected = [access_1['id'], access_2['id']]
|
||||||
self.assertEqual(len(expected), len(actual_access_ids))
|
self.assertEqual(len(expected), len(actual_access_ids))
|
||||||
for pool in expected:
|
for pool in expected:
|
||||||
@ -3649,9 +3649,9 @@ class ShareServerDatabaseAPITestCase(test.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(2, len(servers))
|
self.assertEqual(2, len(servers))
|
||||||
ids = [s['id'] for s in servers]
|
ids = [s['id'] for s in servers]
|
||||||
self.assertTrue(valid['id'] in ids)
|
self.assertIn(valid['id'], ids)
|
||||||
self.assertTrue(other['id'] in ids)
|
self.assertIn(other['id'], ids)
|
||||||
self.assertFalse(invalid['id'] in ids)
|
self.assertNotIn(invalid['id'], ids)
|
||||||
|
|
||||||
def test_get_all_by_host_and_share_subnet_not_found(self):
|
def test_get_all_by_host_and_share_subnet_not_found(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
@ -5615,8 +5615,7 @@ class ResourceLocksTestCase(test.TestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(order_expected,
|
self.assertEqual(order_expected,
|
||||||
[lock['id'] for lock in all_project_locks])
|
[lock['id'] for lock in all_project_locks])
|
||||||
self.assertTrue(lk_5['project_id']
|
self.assertNotIn(lk_5['project_id'], [self.project_id, project_id_2])
|
||||||
not in [self.project_id, project_id_2])
|
|
||||||
self.assertIsNone(count)
|
self.assertIsNone(count)
|
||||||
|
|
||||||
filtered_locks, count = db_api.resource_lock_get_all(
|
filtered_locks, count = db_api.resource_lock_get_all(
|
||||||
|
@ -17,7 +17,6 @@ import ast
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from hacking import core
|
from hacking import core
|
||||||
import pycodestyle
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -221,8 +220,8 @@ class CheckForTransAdd(BaseASTChecker):
|
|||||||
|
|
||||||
|
|
||||||
@core.flake8ext
|
@core.flake8ext
|
||||||
def check_oslo_namespace_imports(physical_line, logical_line, filename):
|
def check_oslo_namespace_imports(logical_line, filename, noqa):
|
||||||
if pycodestyle.noqa(physical_line):
|
if noqa:
|
||||||
return
|
return
|
||||||
if re.match(oslo_namespace_imports, logical_line):
|
if re.match(oslo_namespace_imports, logical_line):
|
||||||
msg = ("M333: '%s' must be used instead of '%s'.") % (
|
msg = ("M333: '%s' must be used instead of '%s'.") % (
|
||||||
|
@ -638,7 +638,7 @@ class HostManagerTestCase(test.TestCase):
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
self.assertTrue(isinstance(res, list))
|
self.assertIsInstance(res, list)
|
||||||
self.assertEqual(len(expected), len(res))
|
self.assertEqual(len(expected), len(res))
|
||||||
for pool in expected:
|
for pool in expected:
|
||||||
self.assertIn(pool, res)
|
self.assertIn(pool, res)
|
||||||
|
@ -144,7 +144,7 @@ class GaneshaConfigTests(test.TestCase):
|
|||||||
# whitespace-split expressions to tokens with
|
# whitespace-split expressions to tokens with
|
||||||
# (equality is forced to be treated as token by
|
# (equality is forced to be treated as token by
|
||||||
# sandwiching in space)
|
# sandwiching in space)
|
||||||
conf = map(lambda l: l.replace("=", " = ").split(), conf)
|
conf = map(lambda line: line.replace("=", " = ").split(), conf)
|
||||||
# get rid of by-product empty lists (derived from superflouous
|
# get rid of by-product empty lists (derived from superflouous
|
||||||
# ";"-s that might have crept in due to "sandwiching")
|
# ";"-s that might have crept in due to "sandwiching")
|
||||||
conf = map(lambda x: x, conf)
|
conf = map(lambda x: x, conf)
|
||||||
|
@ -87,7 +87,7 @@ class GlusterfsShareDriverBaseTestCase(test.TestCase):
|
|||||||
if layout_name is not None:
|
if layout_name is not None:
|
||||||
conf.glusterfs_share_layout = layout_name
|
conf.glusterfs_share_layout = layout_name
|
||||||
if has_snap is None:
|
if has_snap is None:
|
||||||
del(_layout._snapshots_are_supported)
|
del _layout._snapshots_are_supported
|
||||||
else:
|
else:
|
||||||
_layout._snapshots_are_supported = has_snap
|
_layout._snapshots_are_supported = has_snap
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ class GlusterfsVolumeMappedLayoutTestCase(test.TestCase):
|
|||||||
test_args = ('volume', 'list')
|
test_args = ('volume', 'list')
|
||||||
|
|
||||||
def raise_exception(*args, **kwargs):
|
def raise_exception(*args, **kwargs):
|
||||||
if(args == test_args):
|
if args == test_args:
|
||||||
raise exception.GlusterfsException()
|
raise exception.GlusterfsException()
|
||||||
|
|
||||||
self._layout.configuration.glusterfs_servers = [self.glusterfs_server1]
|
self._layout.configuration.glusterfs_servers = [self.glusterfs_server1]
|
||||||
@ -438,8 +438,8 @@ class GlusterfsVolumeMappedLayoutTestCase(test.TestCase):
|
|||||||
self._layout._push_gluster_vol(self.glusterfs_target2)
|
self._layout._push_gluster_vol(self.glusterfs_target2)
|
||||||
|
|
||||||
self.assertEqual(1, len(self._layout.gluster_used_vols))
|
self.assertEqual(1, len(self._layout.gluster_used_vols))
|
||||||
self.assertFalse(
|
self.assertNotIn(self.glusterfs_target2,
|
||||||
self.glusterfs_target2 in self._layout.gluster_used_vols)
|
self._layout.gluster_used_vols)
|
||||||
|
|
||||||
def test_push_gluster_vol_excp(self):
|
def test_push_gluster_vol_excp(self):
|
||||||
self._layout.gluster_used_vols = set([self.glusterfs_target1])
|
self._layout.gluster_used_vols = set([self.glusterfs_target1])
|
||||||
|
@ -286,7 +286,7 @@ HNAS_RESULT_tree_job_status_fail = """JOB ID : d933100a-b5f6-11d0-91d9-836896aad
|
|||||||
Source files missing : 0
|
Source files missing : 0
|
||||||
Source files skipped : 801
|
Source files skipped : 801
|
||||||
Skipping details : 104 symlinks, 452 hard links,
|
Skipping details : 104 symlinks, 452 hard links,
|
||||||
47 block special devices, 25 character devices"""
|
47 block special devices, 25 character devices""" # noqa
|
||||||
|
|
||||||
HNAS_RESULT_job_completed = """JOB ID : ab4211b8-aac8-11ce-91af-39e0822ea368
|
HNAS_RESULT_job_completed = """JOB ID : ab4211b8-aac8-11ce-91af-39e0822ea368
|
||||||
Job request
|
Job request
|
||||||
|
@ -2747,8 +2747,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
@ddt.data(True, False)
|
@ddt.data(True, False)
|
||||||
def test_update_volume_efficiency_attributes(self, status):
|
def test_update_volume_efficiency_attributes(self, status):
|
||||||
response = {
|
response = {
|
||||||
'dedupe': not(status),
|
'dedupe': not status,
|
||||||
'compression': not(status)
|
'compression': not status
|
||||||
}
|
}
|
||||||
self.mock_object(self.client, 'get_volume_efficiency_status',
|
self.mock_object(self.client, 'get_volume_efficiency_status',
|
||||||
mock.Mock(return_value=response))
|
mock.Mock(return_value=response))
|
||||||
@ -6089,7 +6089,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
def test__create_vlan(self, code):
|
def test__create_vlan(self, code):
|
||||||
self.mock_object(self.client, 'send_request',
|
self.mock_object(self.client, 'send_request',
|
||||||
mock.Mock(side_effect=self._mock_api_error(code)))
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
||||||
if not(code):
|
if not code:
|
||||||
self.assertRaises(exception.NetAppException,
|
self.assertRaises(exception.NetAppException,
|
||||||
self.client._create_vlan,
|
self.client._create_vlan,
|
||||||
fake.NODE_NAME,
|
fake.NODE_NAME,
|
||||||
@ -6114,7 +6114,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
mock.Mock(return_value=volume))
|
mock.Mock(return_value=volume))
|
||||||
self.mock_object(self.client, 'send_request',
|
self.mock_object(self.client, 'send_request',
|
||||||
mock.Mock(side_effect=self._mock_api_error(code)))
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
||||||
if not(code):
|
if not code:
|
||||||
self.assertRaises(exception.NetAppException,
|
self.assertRaises(exception.NetAppException,
|
||||||
self.client.delete_fpolicy_event,
|
self.client.delete_fpolicy_event,
|
||||||
fake.SHARE_NAME, 'fake_event')
|
fake.SHARE_NAME, 'fake_event')
|
||||||
@ -6129,7 +6129,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
mock.Mock(return_value=volume))
|
mock.Mock(return_value=volume))
|
||||||
self.mock_object(self.client, 'send_request',
|
self.mock_object(self.client, 'send_request',
|
||||||
mock.Mock(side_effect=self._mock_api_error(code)))
|
mock.Mock(side_effect=self._mock_api_error(code)))
|
||||||
if not(code):
|
if not code:
|
||||||
self.assertRaises(exception.NetAppException,
|
self.assertRaises(exception.NetAppException,
|
||||||
self.client.delete_fpolicy_policy,
|
self.client.delete_fpolicy_policy,
|
||||||
fake.SHARE_NAME, 'fake_policy')
|
fake.SHARE_NAME, 'fake_policy')
|
||||||
@ -6545,7 +6545,7 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
netapp_api.api.NaApiError(code=code)]
|
netapp_api.api.NaApiError(code=code)]
|
||||||
self.mock_object(self.client, 'send_request',
|
self.mock_object(self.client, 'send_request',
|
||||||
mock.Mock(side_effect=responses))
|
mock.Mock(side_effect=responses))
|
||||||
if not(code):
|
if not code:
|
||||||
self.assertRaises(netapp_api.api.NaApiError,
|
self.assertRaises(netapp_api.api.NaApiError,
|
||||||
self.client.remove_cifs_share,
|
self.client.remove_cifs_share,
|
||||||
fake.SHARE_NAME)
|
fake.SHARE_NAME)
|
||||||
|
@ -453,7 +453,7 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
# Asserts
|
# Asserts
|
||||||
self.assertIsNone(retval)
|
self.assertIsNone(retval)
|
||||||
self.assertEqual(share_instance_id, call_args[1]['id'])
|
self.assertEqual(share_instance_id, call_args[1]['id'])
|
||||||
self.assertTrue(isinstance(access_rules_to_be_on_share, list))
|
self.assertIsInstance(access_rules_to_be_on_share, list)
|
||||||
self.assertEqual(len(expected_rules_to_be_on_share),
|
self.assertEqual(len(expected_rules_to_be_on_share),
|
||||||
len(access_rules_to_be_on_share))
|
len(access_rules_to_be_on_share))
|
||||||
for pool in expected_rules_to_be_on_share:
|
for pool in expected_rules_to_be_on_share:
|
||||||
@ -661,7 +661,7 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
|
|
||||||
self.assertIsNone(retval)
|
self.assertIsNone(retval)
|
||||||
self.assertEqual(instance['id'], call_args[1]['id'])
|
self.assertEqual(instance['id'], call_args[1]['id'])
|
||||||
self.assertTrue(isinstance(access_rules_to_be_on_share, list))
|
self.assertIsInstance(access_rules_to_be_on_share, list)
|
||||||
self.assertEqual(len(expected_rules_to_be_on_share),
|
self.assertEqual(len(expected_rules_to_be_on_share),
|
||||||
len(access_rules_to_be_on_share))
|
len(access_rules_to_be_on_share))
|
||||||
for pool in expected_rules_to_be_on_share:
|
for pool in expected_rules_to_be_on_share:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
hacking>=3.1.0,<3.2.0 # Apache-2.0
|
hacking>=6.1.0,<6.2.0 # Apache-2.0
|
||||||
|
|
||||||
coverage>=5.2.1 # Apache-2.0
|
coverage>=5.2.1 # Apache-2.0
|
||||||
ddt>=1.4.1 # MIT
|
ddt>=1.4.1 # MIT
|
||||||
|
Loading…
Reference in New Issue
Block a user