Skip to content

Commit

Permalink
Replace flake8 with ruff (#4314)
Browse files Browse the repository at this point in the history
Ruff is much faster and replicates everything flake8 can do.
Additionally, I added a few more checks that seemed relevant to our
project and would not require many changes.

Long term, we should be able to replace isort with ruff as well,
and maybe eventually pylint, though ruff is not yet able to do
everything that pylint can.
  • Loading branch information
TheRealFalcon authored Aug 4, 2023
1 parent d41264c commit e2ae755
Show file tree
Hide file tree
Showing 26 changed files with 100 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check_format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
env: [flake8, mypy, pylint, black, isort]
env: [ruff, mypy, pylint, black, isort]
lint-with:
- {tip-versions: false, os: ubuntu-20.04}
- {tip-versions: true, os: ubuntu-latest}
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ all: check

check: check_version test yaml

style-check: flake8
style-check: lint

flake8:
@$(CWD)/tools/run-flake8
lint:
@$(CWD)/tools/run-lint

unittest: clean_pyc
$(PYTHON) -m pytest -v tests/unittests cloudinit
Expand Down Expand Up @@ -162,7 +162,7 @@ fix_spelling:
awk -F ': | -> ' '{printf "sed -i \047s/%s/%s/g\047 %s\n", $$2, $$3, $$1}' | \
sh

.PHONY: all check test flake8 clean rpm srpm deb deb-src yaml
.PHONY: all check test lint clean rpm srpm deb deb-src yaml
.PHONY: check_version clean_pyc
.PHONY: unittest style-check fix_spelling render-template benchmark-generator
.PHONY: clean_pytest clean_packaging check_spelling clean_release doc
7 changes: 4 additions & 3 deletions cloudinit/cmd/devel/make_mime.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ def create_mime_message(files):
)
content_type = sub_message.get_content_type().lower()
if content_type not in get_content_types():
msg = (
"content type %r for attachment %s " "may be incorrect!"
) % (content_type, i + 1)
msg = ("content type %r for attachment %s may be incorrect!") % (
content_type,
i + 1,
)
errors.append(msg)
sub_messages.append(sub_message)
combined_message = MIMEMultipart()
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/cmd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def attempt_cmdline_url(path, network=True, cmdline=None) -> Tuple[int, str]:
except KeyError:
return (logging.DEBUG, "No kernel command line url found.")

path_is_local = url.startswith("file://") or url.startswith("/")
path_is_local = url.startswith(("file://", "/"))

if path_is_local and os.path.exists(path):
if network:
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/config/cc_apt_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ def _get_key_files():
key_files = [APT_LOCAL_KEYS] if os.path.isfile(APT_LOCAL_KEYS) else []

for file in os.listdir(APT_TRUSTED_GPG_DIR):
if file.endswith(".gpg") or file.endswith(".asc"):
if file.endswith((".gpg", ".asc")):
key_files.append(APT_TRUSTED_GPG_DIR + file)
return key_files if key_files else ""

Expand Down
2 changes: 1 addition & 1 deletion cloudinit/net/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def is_applicable(self) -> bool:
"""
if self._files:
for item in shlex.split(self._cmdline):
if item.startswith("ip=") or item.startswith("ip6="):
if item.startswith(("ip=", "ip6=")):
return True
if os.path.exists(_OPEN_ISCSI_INTERFACE_FILE):
# iBft can configure networking without ip=
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/net/dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ def get_latest_lease(lease_d=None):
if fname.startswith("dhclient6"):
# avoid files that start with dhclient6 assuming dhcpv6.
continue
if not (fname.endswith(".lease") or fname.endswith(".leases")):
if not (fname.endswith((".lease", ".leases"))):
continue

abs_path = os.path.join(lease_d, fname)
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/net/sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ def _supported_vlan_names(rdev, vid):


def available(target=None):
if not util.system_info()["variant"] in KNOWN_DISTROS:
if util.system_info()["variant"] not in KNOWN_DISTROS:
return False
if available_sysconfig(target):
return True
Expand Down
12 changes: 4 additions & 8 deletions cloudinit/sources/DataSourceAkamai.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class MetadataAvailabilityResult(Enum):


class DataSourceAkamai(sources.DataSource):

dsname = "Akamai"
local_stage = False

Expand Down Expand Up @@ -275,21 +274,18 @@ def _fetch_metadata(self, use_v6: bool = False) -> bool:
"Metadata-Token": token,
},
)
self.userdata_raw = str(userdata) # type: ignore
self.userdata_raw = str(userdata)
try:
self.userdata_raw = b64decode(
self.userdata_raw # type: ignore
).decode()
self.userdata_raw = b64decode(self.userdata_raw).decode()
except binascii.Error as e:
LOG.warning("Failed to base64 decode userdata due to %s", e)
except url_helper.UrlError as e:
# we failed to retrieve data with an exception; log the error and
# return false, indicating that we should retry using a different
# network if possible
LOG.warning(
"Failed to retrieve metadata using IPv%s due to %s" "6"
if use_v6
else "4",
"Failed to retrieve metadata using IPv%s due to %s",
"6" if use_v6 else "4",
e,
)
return False
Expand Down
6 changes: 3 additions & 3 deletions cloudinit/sources/DataSourceVMware.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ def guestinfo_get_value(key, vmware_rpctool=VMWARE_RPCTOOL):
util.logexc(
LOG,
"Unexpected error while trying to get "
+ "guestinfo value for key %s",
"guestinfo value for key %s",
key,
)

Expand Down Expand Up @@ -593,7 +593,7 @@ def guestinfo_set_value(key, value, vmware_rpctool=VMWARE_RPCTOOL):
util.logexc(
LOG,
"Unexpected error while trying to set "
+ "guestinfo key=%s to value=%s",
"guestinfo key=%s to value=%s",
key,
value,
)
Expand All @@ -609,7 +609,7 @@ def guestinfo_redact_keys(keys, vmware_rpctool=VMWARE_RPCTOOL):
"""
if not keys:
return
if not type(keys) in (list, tuple):
if type(keys) not in (list, tuple):
keys = [keys]
for key in keys:
key_name = get_guestinfo_key_name(key)
Expand Down
2 changes: 1 addition & 1 deletion cloudinit/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def __init__(self, sys_cfg, distro: Distro, paths: Paths, ud_proc=None):
self.paths = paths
self.userdata = None
self.metadata: dict = {}
self.userdata_raw = None
self.userdata_raw: Optional[str] = None
self.vendordata = None
self.vendordata2 = None
self.vendordata_raw = None
Expand Down
14 changes: 4 additions & 10 deletions cloudinit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,7 @@ def find_devs_with_netbsd(
for dev in out.stdout.split():
if label or _type:
mscdlabel_out, _ = subp.subp(["mscdlabel", dev], rcs=[0, 1])
if label and not ('label "%s"' % label) in mscdlabel_out:
if label and ('label "%s"' % label) not in mscdlabel_out:
continue
if _type == "iso9660" and "ISO filesystem" not in mscdlabel_out:
continue
Expand Down Expand Up @@ -1444,15 +1444,9 @@ def find_devs_with_dragonflybsd(
]

if criteria == "TYPE=iso9660":
devlist = [
i for i in devlist if i.startswith("cd") or i.startswith("acd")
]
devlist = [i for i in devlist if i.startswith(("cd", "acd"))]
elif criteria in ["LABEL=CONFIG-2", "TYPE=vfat"]:
devlist = [
i
for i in devlist
if not (i.startswith("cd") or i.startswith("acd"))
]
devlist = [i for i in devlist if not (i.startswith(("cd", "acd")))]
elif criteria:
LOG.debug("Unexpected criteria: %s", criteria)
return ["/dev/" + i for i in devlist]
Expand Down Expand Up @@ -2968,7 +2962,7 @@ def get_installed_packages(target=None):
(state, pkg, _) = line.split(None, 2)
except ValueError:
continue
if state.startswith("hi") or state.startswith("ii"):
if state.startswith(("hi", "ii")):
pkgs_inst.add(re.sub(":.*", "", pkg))

return pkgs_inst
Expand Down
4 changes: 2 additions & 2 deletions doc/rtd/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
"light_logo": "logo.png",
"dark_logo": "logo-dark-mode.png",
"light_css_variables": {
"font-stack": "Ubuntu, -apple-system, Segoe UI, Roboto, Oxygen, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif",
"font-stack--monospace": "Ubuntu Mono variable, Ubuntu Mono, Consolas, Monaco, Courier, monospace",
"font-stack": "Ubuntu, -apple-system, Segoe UI, Roboto, Oxygen, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif", # noqa: E501
"font-stack--monospace": "Ubuntu Mono variable, Ubuntu Mono, Consolas, Monaco, Courier, monospace", # noqa: E501
"color-foreground-primary": "#111",
"color-foreground-secondary": "var(--color-foreground-primary)",
"color-foreground-muted": "#333",
Expand Down
54 changes: 38 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,46 @@ skip = ["cloudinit/cmd/main.py", ".tox", "packages", "tools"]
follow_imports = "silent"
warn_unused_ignores = "true"
warn_redundant_casts = "true"
exclude=[]
exclude = []

[[tool.mypy.overrides]]
module = [
"apport.*",
"BaseHTTPServer",
"cloudinit.feature_overrides",
"configobj",
"debconf",
"httplib",
"jsonpatch",
"netifaces",
"paramiko.*",
"pip.*",
"pycloudlib.*",
"responses",
"serial",
"tests.integration_tests.user_settings",
"uaclient.*"
"apport.*",
"BaseHTTPServer",
"cloudinit.feature_overrides",
"configobj",
"debconf",
"httplib",
"jsonpatch",
"netifaces",
"paramiko.*",
"pip.*",
"pycloudlib.*",
"responses",
"serial",
"tests.integration_tests.user_settings",
"uaclient.*",
]
ignore_missing_imports = true

[tool.ruff]
target-version = "py37"
line-length = 79
# E, W, and F make up the entirety of default flake8
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"CPY", # flake8-copyright
"T10", # flake8-debugger
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"G", # flake8-logging-format
"PIE", # flake8-pie
"Q", # flake8-quotes
]
ignore = [
"E731", # Do not assign a `lambda` expression, use a `def`
]
[tool.ruff.per-file-ignores]
"cloudinit/cmd/main.py" = ["E402"]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def render_tmpl(template, mode=None):
that files are different outside of the debian directory."""

# newer versions just use install.
if not ("install" in sys.argv):
if "install" not in sys.argv:
return template

tmpl_ext = ".tmpl"
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests/modules/test_jinja_templating.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_invalid_etc_cloud_substitution(client: IntegrationInstance):
)
client.write_to_file("/etc/cloud/cloud.cfg.d/50-no-var.cfg", no_var_part)

normal_part = "bootcmd:\n" " - echo hi > /var/tmp/bootcmd_output\n"
normal_part = "bootcmd:\n - echo hi > /var/tmp/bootcmd_output\n"
client.write_to_file("/etc/cloud/cloud.cfg.d/60-normal.cfg", normal_part)

client.execute("cloud-init clean --logs")
Expand Down
4 changes: 2 additions & 2 deletions tests/integration_tests/test_upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud):
pre_analyze_totals = [
x
for x in pre_cloud_analyze.splitlines()
if x.startswith("Finished stage") or x.startswith("Total Time")
if x.startswith(("Finished stage", "Total Time"))
]
post_analyze_totals = [
x
for x in post_cloud_analyze.splitlines()
if x.startswith("Finished stage") or x.startswith("Total Time")
if x.startswith(("Finished stage", "Total Time"))
]

# pylint: disable=logging-format-interpolation
Expand Down
5 changes: 2 additions & 3 deletions tests/unittests/cmd/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def setup_mocks(mocker):

@mock.patch(M_PATH + "addLogHandlerCLI", lambda *args: "")
class TestQuery:

Args = namedtuple(
"Args",
"debug dump_all format instance_data list_keys user_data vendor_data"
Expand Down Expand Up @@ -531,7 +530,7 @@ def test_handle_args_list_keys_sorts_nested_keys_when_varname(
instance_data = tmpdir.join("instance-data")
instance_data.write(
'{"v1": {"v1_1": "val1.1", "v1_2": "val1.2"}, "v2":'
+ ' {"v2_2": "val2.2"}, "top": "gun"}'
' {"v2_2": "val2.2"}, "top": "gun"}'
)
expected = "v1_1\nv1_2\n"
args = self.Args(
Expand All @@ -557,7 +556,7 @@ def test_handle_args_list_keys_errors_when_varname_is_not_a_dict(
instance_data = tmpdir.join("instance-data")
instance_data.write(
'{"v1": {"v1_1": "val1.1", "v1_2": "val1.2"}, "v2": '
+ '{"v2_2": "val2.2"}, "top": "gun"}'
'{"v2_2": "val2.2"}, "top": "gun"}'
)
expected_error = "--list-keys provided but 'top' is not a dict"
args = self.Args(
Expand Down
8 changes: 4 additions & 4 deletions tests/unittests/config/test_cc_disk_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,14 @@ def test_with_cmd(self, subp, *args):

self.assertIn(
"extra_opts "
+ "ignored because cmd was specified: mkfs -t ext4 -L with_cmd "
+ "/dev/xdb1",
"ignored because cmd was specified: mkfs -t ext4 -L with_cmd "
"/dev/xdb1",
self.logs.getvalue(),
)
self.assertIn(
"overwrite "
+ "ignored because cmd was specified: mkfs -t ext4 -L with_cmd "
+ "/dev/xdb1",
"ignored because cmd was specified: mkfs -t ext4 -L with_cmd "
"/dev/xdb1",
self.logs.getvalue(),
)

Expand Down
5 changes: 2 additions & 3 deletions tests/unittests/config/test_cc_ntp.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@


class TestNtp(FilesystemMockingTestCase):

with_logs = True

def setUp(self):
Expand Down Expand Up @@ -349,8 +348,8 @@ def test_ntp_handler_real_distro_ntp_templates(self):
)
expected_content = (
"# cloud-init generated file\n"
+ "# See timesyncd.conf(5) for details.\n\n"
+ "[Time]\nNTP=%s %s \n"
"# See timesyncd.conf(5) for details.\n\n"
"[Time]\nNTP=%s %s \n"
% (expected_servers, expected_pools)
)
self.assertEqual(expected_content, content)
Expand Down
3 changes: 1 addition & 2 deletions tests/unittests/config/test_cc_puppet.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ def test_enable_fallback_on_failure(self, m_subp):

@mock.patch("cloudinit.config.cc_puppet._manage_puppet_services")
class TestPuppetHandle(CiTestCase):

with_logs = True

def setUp(self):
Expand Down Expand Up @@ -464,7 +463,7 @@ class TestInstallPuppetAio:
[mock.call([mock.ANY, "--cleanup"], capture=False)],
[
mock.call(
url="https://raw.githubusercontent.com/puppetlabs/install-puppet/main/install.sh", # noqa: 501
url="https://raw.githubusercontent.com/puppetlabs/install-puppet/main/install.sh", # noqa: E501
retries=5,
)
],
Expand Down
2 changes: 1 addition & 1 deletion tests/unittests/config/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ def test_validateconfig_schema_emits_warning_on_missing_jsonschema(
):
"""Warning from validate_cloudconfig_schema when missing jsonschema."""
schema = {"properties": {"p1": {"type": "string"}}}
with mock.patch.dict("sys.modules", **{"jsonschema": ImportError()}):
with mock.patch.dict("sys.modules", jsonschema=ImportError()):
validate_cloudconfig_schema({"p1": -1}, schema, strict=True)
assert "Ignoring schema validation. jsonschema is not present" in (
caplog.text
Expand Down
Loading

0 comments on commit e2ae755

Please sign in to comment.