Browse Source

Bump pylint version to support python 3.8

As spotted in Focal testing patch [0], pep8 test fails with many
C0321 false-positives, reported in pylint as current version does not
support python 3.8 [1]

Use a newer version of pylint and astroid, fixing or disabling some of
the new checks: no-else-*, unnecessary-comprehension, import-outside-toplevel

[0] https://review.opendev.org/#/c/738163/
[1] https://github.com/PyCQA/pylint/issues/2737

Change-Id: Ie646b7093aa8634fd950c136a0eba9adcf56591c
changes/11/744211/4
Bernard Cafarelli 1 year ago
parent
commit
cebdd77af8
No known key found for this signature in database GPG Key ID: 9531F08245465A52
  1. 3
      neutron/agent/common/async_process.py
  2. 3
      neutron/agent/linux/dhcp.py
  3. 22
      neutron/agent/linux/iptables_firewall.py
  4. 2
      neutron/agent/linux/iptables_manager.py
  5. 4
      neutron/agent/linux/l3_tc_lib.py
  6. 1
      neutron/agent/linux/openvswitch_firewall/iptables.py
  7. 2
      neutron/agent/ovsdb/native/connection.py
  8. 15
      neutron/api/extensions.py
  9. 2
      neutron/api/rpc/handlers/securitygroups_rpc.py
  10. 1
      neutron/cmd/sanity/checks.py
  11. 1
      neutron/common/eventlet_utils.py
  12. 2
      neutron/common/ovn/utils.py
  13. 2
      neutron/conf/db/migration_cli.py
  14. 20
      neutron/db/agentschedulers_db.py
  15. 8
      neutron/db/db_base_plugin_v2.py
  16. 4
      neutron/db/l3_db.py
  17. 15
      neutron/db/migration/autogen.py
  18. 6
      neutron/db/securitygroups_db.py
  19. 15
      neutron/ipam/drivers/neutrondb_ipam/driver.py
  20. 1
      neutron/objects/__init__.py
  21. 3
      neutron/objects/network_segment_range.py
  22. 2
      neutron/objects/qos/qos_policy_validator.py
  23. 2
      neutron/objects/router.py
  24. 2
      neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
  25. 2
      neutron/plugins/ml2/managers.py
  26. 4
      neutron/plugins/ml2/plugin.py
  27. 12
      neutron/privileged/agent/linux/ip_lib.py
  28. 2
      neutron/services/conntrack_helper/plugin.py
  29. 1
      neutron/services/logapi/drivers/base.py
  30. 1
      neutron/services/logapi/drivers/openvswitch/ovs_firewall_log.py
  31. 2
      neutron/services/qos/drivers/ovn/driver.py
  32. 5
      neutron/services/trunk/plugin.py
  33. 5
      neutron/services/trunk/rpc/server.py
  34. 2
      neutron/services/trunk/rules.py
  35. 6
      test-requirements.txt

3
neutron/agent/common/async_process.py

@ -112,8 +112,7 @@ class AsyncProcess(object):
LOG.debug('Launching async process [%s].', self.cmd)
if self._is_running:
raise AsyncProcessException(_('Process is already started'))
else:
self._spawn()
self._spawn()
if block:
common_utils.wait_until_true(self.is_active)

3
neutron/agent/linux/dhcp.py

@ -951,13 +951,12 @@ class Dnsmasq(DhcpLocalProcess):
if line.startswith('duid'):
if not server_id:
server_id = line.strip().split()[1]
continue
else:
LOG.warning('Multiple DUID entries in %s '
'lease file, dnsmasq is possibly '
'not functioning properly',
filename)
continue
continue
parts = line.strip().split()
if len(parts) != 5:
LOG.warning('Invalid lease entry %s found in %s '

22
neutron/agent/linux/iptables_firewall.py

@ -429,18 +429,16 @@ class IptablesFirewallDriver(firewall.FirewallDriver):
if self.enable_ipset:
port_sg_rules.append(rule)
break
else:
port_sg_rules.extend(
self._expand_sg_rule_with_remote_ips(
rule, port, constants.INGRESS_DIRECTION))
if port_sg_rules:
break
else:
port_sg_rules.extend(
self._expand_sg_rule_with_remote_ips(
rule, port, constants.EGRESS_DIRECTION))
if port_sg_rules:
break
port_sg_rules.extend(
self._expand_sg_rule_with_remote_ips(
rule, port, constants.INGRESS_DIRECTION))
if port_sg_rules:
break
port_sg_rules.extend(
self._expand_sg_rule_with_remote_ips(
rule, port, constants.EGRESS_DIRECTION))
if port_sg_rules:
break
return port_sg_rules
@staticmethod

2
neutron/agent/linux/iptables_manager.py

@ -848,7 +848,7 @@ def _generate_chain_diff_iptables_commands(chain, old_chain_rules,
if line.startswith('?'):
# skip ? because that's a guide string for intraline differences
continue
elif line.startswith('-'): # line deleted
if line.startswith('-'): # line deleted
statements.append('-D %s %d' % (chain, old_index))
# since we are removing a line from the old rules, we
# backup the index by 1

4
neutron/agent/linux/l3_tc_lib.py

@ -69,14 +69,14 @@ class FloatingIPTcCommandBase(ip_lib.IPDevice):
filter_id = m.group(2)
# It matched, so ip/32 is not here. continue
continue
elif not line.startswith('match'):
if not line.startswith('match'):
continue
parts = line.split(" ")
if ip + '/32' in parts:
filterids_for_ip.append(filter_id)
if len(filterids_for_ip) > 1:
raise exceptions.MultipleFilterIDForIPFound(ip=ip)
elif len(filterids_for_ip) == 0:
if len(filterids_for_ip) == 0:
raise exceptions.FilterIDForIPNotFound(ip=ip)
return filterids_for_ip[0]

1
neutron/agent/linux/openvswitch_firewall/iptables.py

@ -22,6 +22,7 @@ def get_device_port_name(port_id):
def get_iptables_driver_instance():
"""Load hybrid iptables firewall driver."""
# pylint: disable=import-outside-toplevel
from neutron.agent.linux import iptables_firewall
class HybridIptablesHelper(

2
neutron/agent/ovsdb/native/connection.py

@ -51,7 +51,7 @@ def configure_ssl_conn():
for ssl_opt, ssl_file in req_ssl_opts.items():
if not ssl_file:
raise ovsdb_exc.OvsdbSslRequiredOptError(ssl_opt=ssl_opt)
elif not os.path.exists(ssl_file):
if not os.path.exists(ssl_file):
raise ovsdb_exc.OvsdbSslConfigNotFound(ssl_file=ssl_file)
# TODO(ihrachys): move to ovsdbapp
Stream.ssl_set_private_key_file(req_ssl_opts['ssl_key_file'])

15
neutron/api/extensions.py

@ -394,14 +394,13 @@ class ExtensionManager(object):
if not faulty_extensions <= default_extensions:
raise exceptions.ExtensionsNotFound(
extensions=list(faulty_extensions))
else:
# Remove the faulty extensions so that they do not show during
# ext-list
for ext in faulty_extensions:
try:
del self.extensions[ext]
except KeyError:
pass
# Remove the faulty extensions so that they do not show during
# ext-list
for ext in faulty_extensions:
try:
del self.extensions[ext]
except KeyError:
pass
def _check_extension(self, extension):
"""Checks for required methods in extension objects."""

2
neutron/api/rpc/handlers/securitygroups_rpc.py

@ -288,7 +288,7 @@ class SecurityGroupServerAPIShim(sg_rpc_base.SecurityGroupInfoAPIMixin):
'allowed_address_pairs'}):
# none of the relevant fields to SG calculations changed
return
sgs.update({sg_id for sg_id in updated.security_group_ids})
sgs.update(set(updated.security_group_ids))
if sgs:
self._sg_agent.security_groups_member_updated(sgs)

1
neutron/cmd/sanity/checks.py

@ -81,6 +81,7 @@ def patch_supported():
def nova_notify_supported():
try:
# pylint:disable=import-outside-toplevel
import neutron.notifiers.nova # noqa since unused
return True
except ImportError:

1
neutron/common/eventlet_utils.py

@ -40,6 +40,7 @@ def monkey_patch():
# Monkey patch the original current_thread to use the up-to-date _active
# global variable. See https://bugs.launchpad.net/bugs/1863021 and
# https://github.com/eventlet/eventlet/issues/592
# pylint: disable=import-outside-toplevel
import __original_module_threading as orig_threading
import threading # noqa
orig_threading.current_thread.__globals__['_active'] = threading._active

2
neutron/common/ovn/utils.py

@ -420,7 +420,7 @@ def get_system_dns_resolvers(resolver_file=DNS_RESOLVER_FILE):
def get_port_subnet_ids(port):
fixed_ips = [ip for ip in port['fixed_ips']]
fixed_ips = list(port['fixed_ips'])
return [f['subnet_id'] for f in fixed_ips]

2
neutron/conf/db/migration_cli.py

@ -22,7 +22,7 @@ migration_entrypoints = {
for entrypoint in pkg_resources.iter_entry_points(MIGRATION_ENTRYPOINTS)
}
INSTALLED_SUBPROJECTS = [project_ for project_ in migration_entrypoints]
INSTALLED_SUBPROJECTS = list(migration_entrypoints)
CORE_OPTS = [
cfg.StrOpt('subproject',

20
neutron/db/agentschedulers_db.py

@ -146,15 +146,14 @@ class AgentSchedulerDbMixin(agents_db.AgentDbMixin):
binding_resource_id = getattr(binding, resource_id_attr)
if binding_agent_id in agents_back_online:
continue
else:
# we need new context to make sure we use different DB
# transaction - otherwise we may fetch same agent record
# each time due to REPEATABLE_READ isolation level
context = ncontext.get_admin_context()
agent = self._get_agent(context, binding_agent_id)
if agent.is_active:
agents_back_online.add(binding_agent_id)
continue
# we need new context to make sure we use different DB
# transaction - otherwise we may fetch same agent record
# each time due to REPEATABLE_READ isolation level
context = ncontext.get_admin_context()
agent = self._get_agent(context, binding_agent_id)
if agent.is_active:
agents_back_online.add(binding_agent_id)
continue
LOG.warning(
"Rescheduling %(resource_name)s %(resource)s from agent "
@ -299,8 +298,7 @@ class DhcpAgentSchedulerDbMixin(dhcpagentscheduler
down_bindings = network.NetworkDhcpAgentBinding.get_down_bindings(
context, cutoff)
dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP)
dead_bindings = [b for b in
self._filter_bindings(context, down_bindings)]
dead_bindings = list(self._filter_bindings(context, down_bindings))
agents = self.get_agent_objects(
context, {'agent_type': [constants.AGENT_TYPE_DHCP]})
if not agents:

8
neutron/db/db_base_plugin_v2.py

@ -158,6 +158,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
cfg.CONF.notify_nova_on_port_data_changes):
# Import nova conditionally to support the use case of Neutron
# being used outside of an OpenStack context.
# pylint: disable=import-outside-toplevel
from neutron.notifiers import nova
self.nova_notifier = nova.Notifier.get_instance()
# NOTE(arosen) These event listeners are here to hook into when
@ -172,6 +173,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
self.nova_notifier.record_port_status_changed)
if cfg.CONF.ironic.enable_notifications:
# Import ironic notifier conditionally
# pylint: disable=import-outside-toplevel
from neutron.notifiers import ironic
self.ironic_notifier = ironic.Notifier.get_instance()
@ -600,11 +602,11 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
error_message = _("Multicast IP subnet is not supported "
"if enable_dhcp is True")
raise exc.InvalidInput(error_message=error_message)
elif net.is_loopback():
if net.is_loopback():
error_message = _("Loopback IP subnet is not supported "
"if enable_dhcp is True")
raise exc.InvalidInput(error_message=error_message)
elif ip_ver == constants.IP_VERSION_4 and net.first == 0:
if ip_ver == constants.IP_VERSION_4 and net.first == 0:
error_message = _("First IP '0.0.0.0' of network is not "
"supported if enable_dhcp is True.")
raise exc.InvalidInput(error_message=error_message)
@ -1636,7 +1638,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
return {'prefixes': subnetpool.prefixes}
all_prefix_set = netaddr.IPSet(subnetpool.prefixes)
removal_prefix_set = netaddr.IPSet([x for x in prefixes])
removal_prefix_set = netaddr.IPSet(list(prefixes))
if all_prefix_set.isdisjoint(removal_prefix_set):
# The prefixes requested for removal are not in the prefix
# list making this a no-op, so simply return.

4
neutron/db/l3_db.py

@ -637,7 +637,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
msg = _('Router port must have at least one fixed IP')
raise n_exc.BadRequest(resource='router', msg=msg)
fixed_ips = [ip for ip in port['fixed_ips']]
fixed_ips = list(port['fixed_ips'])
for fixed_ip in fixed_ips:
subnet = self._core_plugin.get_subnet(
context, fixed_ip['subnet_id'])
@ -684,7 +684,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
'p': existing_port['id'],
'nid': existing_port['network_id']})
fixed_ips = [ip for ip in port['fixed_ips']]
fixed_ips = list(port['fixed_ips'])
subnets = []
for fixed_ip in fixed_ips:
subnet = self._core_plugin.get_subnet(context,

15
neutron/db/migration/autogen.py

@ -23,9 +23,7 @@ _ec_dispatcher = Dispatcher()
def process_revision_directives(context, revision, directives):
directives[:] = [
directive for directive in _assign_directives(context, directives)
]
directives[:] = list(_assign_directives(context, directives))
def _assign_directives(context, directives, phase=None):
@ -59,10 +57,9 @@ def _migration_script_ops(context, directive, phase):
op = ops.MigrationScript(
new_rev_id(),
ops.UpgradeOps(ops=[
d for d in _assign_directives(
context, directive.upgrade_ops.ops, phase)
]),
ops.UpgradeOps(ops=list(
_assign_directives(context, directive.upgrade_ops.ops, phase)
)),
ops.DowngradeOps(ops=[]),
message=directive.message,
**autogen_kwargs
@ -116,9 +113,7 @@ def _alter_column(context, directive, phase):
def _modify_table_ops(context, directive, phase):
op = ops.ModifyTableOps(
directive.table_name,
ops=[
d for d in _assign_directives(context, directive.ops, phase)
],
ops=list(_assign_directives(context, directive.ops, phase)),
schema=directive.schema)
if not op.is_empty():
return op

6
neutron/db/securitygroups_db.py

@ -515,9 +515,9 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase,
ip_proto in const.SG_PORT_PROTO_NAMES):
if rule['port_range_min'] == 0 or rule['port_range_max'] == 0:
raise ext_sg.SecurityGroupInvalidPortValue(port=0)
elif (rule['port_range_min'] is not None and
rule['port_range_max'] is not None and
rule['port_range_min'] <= rule['port_range_max']):
if (rule['port_range_min'] is not None and
rule['port_range_max'] is not None and
rule['port_range_min'] <= rule['port_range_max']):
# When min/max are the same it is just a single port
pass
else:

15
neutron/ipam/drivers/neutrondb_ipam/driver.py

@ -209,14 +209,13 @@ class NeutronDbSubnet(ipam_base.Subnet):
if window < allocated_num_addresses:
continue
else:
# Maximize randomness by using the random module's built in
# sampling function
av_ips = list(itertools.islice(av_set, 0, window))
allocated_ip_pool = random.sample(av_ips,
allocated_num_addresses)
allocated_ips.extend([str(allocated_ip)
for allocated_ip in allocated_ip_pool])
# Maximize randomness by using the random module's built in
# sampling function
av_ips = list(itertools.islice(av_set, 0, window))
allocated_ip_pool = random.sample(av_ips,
allocated_num_addresses)
allocated_ips.extend([str(allocated_ip)
for allocated_ip in allocated_ip_pool])
requested_num_addresses -= allocated_num_addresses
if requested_num_addresses:

1
neutron/objects/__init__.py

@ -16,6 +16,7 @@ import sys
def register_objects():
# local import to avoid circular import failure
# pylint: disable=import-outside-toplevel
from neutron.common import utils
dirn = os.path.dirname(sys.modules[__name__].__file__)
utils.import_modules_recursively(dirn)

3
neutron/objects/network_segment_range.py

@ -148,8 +148,7 @@ class NetworkSegmentRange(base.NeutronDbObject):
.filter(
segments_model.NetworkSegment.network_id ==
models_v2.Network.id)).all()
return {segmentation_id: project_id
for segmentation_id, project_id in alloc_used}
return dict(alloc_used)
@classmethod
def _build_query_segments(cls, context, model, network_type, **filters):

2
neutron/objects/qos/qos_policy_validator.py

@ -28,7 +28,7 @@ def check_bandwidth_rule_conflict(policy, rule_data):
if rule.rule_type == qos_consts.RULE_TYPE_DSCP_MARKING:
# Skip checks if Rule is DSCP
continue
elif rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
if "max_kbps" in rule_data and (
int(rule.min_kbps) > int(rule_data["max_kbps"])):
raise qos_exc.QoSRuleParameterConflict(

2
neutron/objects/router.py

@ -124,7 +124,7 @@ class RouterExtraAttributes(base.NeutronDbObject):
query = (context.session.query(l3.Router, sub_query.c.count).
outerjoin(sub_query))
return [(router, agent_count) for router, agent_count in query]
return list(query)
@base.NeutronObjectRegistry.register

2
neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

@ -1876,7 +1876,7 @@ class OVNClient(object):
port, subnet['ip_version'])
if lsp_dhcp_disabled:
continue
elif not lsp_dhcp_opts:
if not lsp_dhcp_opts:
lsp_dhcp_options = subnet_dhcp_option
else:
port_dhcp_options = copy.deepcopy(dhcp_options)

2
neutron/plugins/ml2/managers.py

@ -238,7 +238,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
if network_type != constants.TYPE_VLAN:
msg = (_('Only VLAN type networks can be updated.'))
raise exc.InvalidInput(error_message=msg)
elif not segmentation_id:
if not segmentation_id:
msg = (_('Only %s field can be updated in VLAN type networks') %
api.SEGMENTATION_ID)
raise exc.InvalidInput(error_message=msg)

4
neutron/plugins/ml2/plugin.py

@ -1597,7 +1597,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
if self._check_update_has_allowed_address_pairs(port):
# has address pairs in request
raise addr_exc.AddressPairAndPortSecurityRequired()
elif not self._check_update_deletes_allowed_address_pairs(port):
if not self._check_update_deletes_allowed_address_pairs(port):
# not a request for deleting the address-pairs
updated_port[addr_apidef.ADDRESS_PAIRS] = (
self.get_allowed_address_pairs(context, id))
@ -1611,7 +1611,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# security groups, port security is set
if self._check_update_has_security_groups(port):
raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups()
elif not self._check_update_deletes_security_groups(port):
if not self._check_update_deletes_security_groups(port):
if not extensions.is_extension_supported(self, 'security-group'):
return
# Update did not have security groups passed in. Check

12
neutron/privileged/agent/linux/ip_lib.py

@ -245,10 +245,9 @@ def get_link_id(device, namespace, raise_exception=True):
if not link_id or len(link_id) < 1:
if raise_exception:
raise NetworkInterfaceNotFound(device=device, namespace=namespace)
else:
LOG.debug('Interface %(dev)s not found in namespace %(namespace)s',
{'dev': device, 'namespace': namespace})
return None
LOG.debug('Interface %(dev)s not found in namespace %(namespace)s',
{'dev': device, 'namespace': namespace})
return None
return link_id[0]
@ -640,9 +639,8 @@ def list_ip_rules(namespace, ip_version, match=None, **kwargs):
family=_IP_VERSION_FAMILY_MAP[ip_version],
match=match, **kwargs))
for rule in rules:
rule['attrs'] = {
key: value for key, value
in ((item[0], item[1]) for item in rule['attrs'])}
rule['attrs'] = dict(
(item[0], item[1]) for item in rule['attrs'])
return rules
except OSError as e:

2
neutron/services/conntrack_helper/plugin.py

@ -117,7 +117,7 @@ class Plugin(l3_conntrack_helper.ConntrackHelperPluginBase):
def _check_conntrack_helper_constraints(self, cth_obj):
if cth_obj.helper not in self.constraints:
raise cth_exc.ConntrackHelperNotAllowed(helper=cth_obj.helper)
elif cth_obj.protocol not in self.constraints[cth_obj.helper]:
if cth_obj.protocol not in self.constraints[cth_obj.helper]:
raise cth_exc.InvalidProtocolForHelper(
helper=cth_obj.helper, protocol=cth_obj.protocol,
supported_protocols=', '.join(

1
neutron/services/logapi/drivers/base.py

@ -50,6 +50,7 @@ class DriverBase(object):
# the log driver.
@registry.receives(log_const.LOGGING_PLUGIN, [events.AFTER_INIT])
def _register(self, resource, event, trigger, payload=None):
# pylint: disable=using-constant-test
if self.is_loaded:
# trigger is the LoggingServiceDriverManager
trigger.register_driver(self)

1
neutron/services/logapi/drivers/openvswitch/ovs_firewall_log.py

@ -49,6 +49,7 @@ REMOTE_RULE_PRIORITY = 70
def setup_logging():
log_file = cfg.CONF.network_log.local_output_log_base
if log_file:
# pylint: disable=import-outside-toplevel
from logging import handlers as watch_handler
log_file_handler = watch_handler.WatchedFileHandler(log_file)
log_file_handler.setLevel(

2
neutron/services/qos/drivers/ovn/driver.py

@ -55,6 +55,8 @@ class OVNQosDriver(base.DriverBase):
@property
def is_loaded(self):
# TODO(bcafarel): should be fixed in DriverBase in neutron-lib
# pylint:disable=invalid-overridden-method
return OVN_QOS in cfg.CONF.ml2.extension_drivers
def update_policy(self, context, policy):

5
neutron/services/trunk/plugin.py

@ -85,7 +85,7 @@ class TrunkPlugin(service_base.ServicePluginBase):
for port in ports:
subports[port['id']]['mac_address'] = port['mac_address']
trunk_details = {'trunk_id': port_db.trunk_port.id,
'sub_ports': [x for x in subports.values()]}
'sub_ports': list(subports.values())}
port_res['trunk_details'] = trunk_details
return port_res
@ -318,8 +318,7 @@ class TrunkPlugin(service_base.ServicePluginBase):
# back to ACTIVE or ERROR.
if trunk.status == constants.TRUNK_ERROR_STATUS:
raise trunk_exc.TrunkInErrorState(trunk_id=trunk_id)
else:
trunk.update(status=constants.TRUNK_DOWN_STATUS)
trunk.update(status=constants.TRUNK_DOWN_STATUS)
for subport in subports:
obj = trunk_objects.SubPort(

5
neutron/services/trunk/rpc/server.py

@ -111,9 +111,8 @@ class TrunkSkeleton(object):
if try_cnt < db_api.MAX_RETRIES - 1:
LOG.debug("Got StaleDataError exception: %s", e)
continue
else:
# re-raise when all tries failed
raise
# re-raise when all tries failed
raise
def update_trunk_status(self, context, trunk_id, status):
"""Update the trunk status to reflect outcome of data plane wiring."""

2
neutron/services/trunk/rules.py

@ -132,7 +132,7 @@ class TrunkPortValidator(object):
]
if len(drivers) > 1:
raise trunk_exc.TrunkPluginDriverConflict()
elif len(drivers) == 1:
if len(drivers) == 1:
return drivers[0].can_trunk_bound_port
else:
return False

6
test-requirements.txt

@ -15,10 +15,8 @@ oslotest>=3.2.0 # Apache-2.0
stestr>=1.0.0 # Apache-2.0
reno>=3.1.0 # Apache-2.0
ddt>=1.0.1 # MIT
astroid==1.6.5;python_version<"3.0" # LGPLv2.1
astroid==2.1.0;python_version>="3.0" # LGPLv2.1
pylint==1.9.2;python_version<"3.0" # GPLv2
pylint==2.2.0;python_version>="3.0" # GPLv2
astroid==2.3.3 # LGPLv2.1
pylint==2.4.4 # GPLv2
isort==4.3.21 # MIT
# Needed to run DB commands in virtualenvs
PyMySQL>=0.7.6 # MIT License

Loading…
Cancel
Save