Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .flake8

This file was deleted.

6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ jobs:
python-version: "3.13"
- name: Install uv
uses: astral-sh/setup-uv@v6
- run: uv sync --group test --no-default-groups
- run: uv run flake8
- uses: astral-sh/ruff-action@v3
- uses: astral-sh/ruff-action@v3
with:
args: "format --check"

type-check:
runs-on: ubuntu-24.04
Expand Down
3 changes: 2 additions & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ uv sync

### Code Style & Type Checking

Code style is enforced via Black, isort and flake8. Types are checked with mypy currently, and pyright is recommended for local development though currently optional. There is a script to run the linting & type-checking:
Code style is enforced via [ruff](https://docs.astral.sh/ruff/). Types are checked with mypy currently, and pyright is
recommended for local development though currently optional. There is a script to run the linting & type-checking:

```sh
scripts/dev-lint.sh
Expand Down
18 changes: 5 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,13 @@ test = [
"coverage>=7.7.1,<8",
"pytest-cov>=7.0.0,<8",
"pytest-testinfra>=10.2.2,<11",
"black>=25.1.0,<26",
"isort>=6.0.1,<7",
"flake8>=7.1.2,<8",
"flake8-black>=0.3.6,<0.4",
"flake8-isort==6.1.2,<7",
"pyyaml>=6.0.2,<7",
"mypy==1.17.1",
"types-cryptography>=3.3.23.2,<4",
"types-paramiko>=2.7,<4",
"types-python-dateutil>2,<3",
"types-PyYAML>6,<7",
"ruff>=0.13.1",
"pyinfra-testgen==0.1.1",
]

Expand All @@ -80,7 +76,7 @@ dev = [
"ipdb",
"ipdbplugin",
# dev-only lint extras
"flake8-spellcheck==0.28.0",
"typos>=1.36.2",
"redbaron",
]

Expand Down Expand Up @@ -108,15 +104,11 @@ packages = ["src/pyinfra", "src/pyinfra_cli"]
[tool.hatch.version]
source = "uv-dynamic-versioning"

[tool.black]
[tool.ruff]
line-length = 100
target-version = ["py38"]

[tool.isort]
profile = "black"
line_length = 100
combine_as_imports = true
include_trailing_comma = true
[tool.ruff.lint.isort]
combine-as-imports = true

[tool.mypy]
check_untyped_defs = true
Expand Down
8 changes: 4 additions & 4 deletions scripts/dev-lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

set -euo pipefail

echo "Execute black..."
uv run black ./
echo "Execute ruff format..."
uv run ruff format

echo "Execute flake8..."
uv run flake8
echo "Execute ruff check..."
uv run ruff check

echo "Execute mypy..."
uv run mypy
Expand Down
2 changes: 1 addition & 1 deletion scripts/spellcheck.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash

flake8 --select=SC --ignore=
uv run typos
3 changes: 1 addition & 2 deletions src/pyinfra/api/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ def __repr__(self) -> str:
f"commands={len(self._commands)}{retry_info})"
)
return (
"OperationMeta(executed=False, "
f"maybeChange={self._maybe_is_change}, hash={self._hash})"
f"OperationMeta(executed=False, maybeChange={self._maybe_is_change}, hash={self._hash})"
)

# Completion & status checks
Expand Down
1 change: 0 additions & 1 deletion src/pyinfra/api/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ def get_caller_frameinfo(frame_offset: int = 0):


def get_operation_order_from_stack(state: "State"):

stack_items = list(reversed(stack()))

i = 0
Expand Down
10 changes: 7 additions & 3 deletions src/pyinfra/connectors/chroot.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ def make_names_data(name: Optional[str] = None):

show_warning()

yield "@chroot/{0}".format(name), {
"chroot_directory": "/{0}".format(name.lstrip("/")),
}, ["@chroot"]
yield (
"@chroot/{0}".format(name),
{
"chroot_directory": "/{0}".format(name.lstrip("/")),
},
["@chroot"],
)

@override
def connect(self) -> None:
Expand Down
4 changes: 1 addition & 3 deletions src/pyinfra/connectors/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ def _start_docker_image(self, image_name):
return local.shell(
f"{self.docker_cmd} run -d {image_name} tail -f /dev/null",
splitlines=True,
)[
-1
] # last line is the container ID
)[-1] # last line is the container ID
except PyinfraError as e:
raise ConnectError(e.args[0])

Expand Down
3 changes: 1 addition & 2 deletions src/pyinfra/connectors/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def _connect(self) -> None:

logger.warning("WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!")
logger.warning(
("Someone could be eavesdropping on you right now " "(man-in-the-middle attack)!"),
("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"),
)
logger.warning("If this is expected, you can remove the bad key using:")
logger.warning(f" ssh-keygen -R {remove_entry}")
Expand Down Expand Up @@ -391,7 +391,6 @@ def get_file_transfer_connection(self) -> FileTransferClient | None:
),
)
except SSHException as e:

raise ConnectError(
(
"Unable to establish SFTP connection. Check that the SFTP subsystem "
Expand Down
7 changes: 1 addition & 6 deletions src/pyinfra/facts/freebsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,7 @@ def command(self, srvname: str, jail: Optional[str] = None) -> StringCommand:
jail = ""

return make_formatted_string_command(
(
"service -j {0} {1} status > /dev/null 2>&1; "
"if [ $? -eq 0 ]; then "
"echo running; "
"fi"
),
("service -j {0} {1} status > /dev/null 2>&1; if [ $? -eq 0 ]; then echo running; fi"),
QuoteString(jail),
QuoteString(srvname),
)
Expand Down
4 changes: 2 additions & 2 deletions src/pyinfra/facts/selinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def command(self, target):

@override
def process(self, output):
# example output: /etc all files system_u:object_r:etc_t:s0 # noqa: SC100
# example output: /etc all files system_u:object_r:etc_t:s0
# but lines at end that won't match: /etc/systemd/system = /usr/lib/systemd/system
if len(output) != 1:
return self.default()
Expand All @@ -104,7 +104,7 @@ class SEPorts(FactBase):
"""

default = dict
# example output: amqp_port_t tcp 15672, 5671-5672 # noqa: SC100
# example output: amqp_port_t tcp 15672, 5671-5672
_regex = re.compile(r"^([\w_]+)\s+(\w+)\s+([\w\-,\s]+)$")

@override
Expand Down
6 changes: 5 additions & 1 deletion src/pyinfra/operations/choco.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ def install():
Install ``choco`` (Chocolatey).
"""

yield "Set-ExecutionPolicy Bypass -Scope Process -Force ;" "iex ((New-Object System.Net.WebClient).DownloadString" '("https://chocolatey.org/install.ps1"))' # noqa
yield (
"Set-ExecutionPolicy Bypass -Scope Process -Force ;"
"iex ((New-Object System.Net.WebClient).DownloadString"
'("https://chocolatey.org/install.ps1"))'
) # noqa
1 change: 0 additions & 1 deletion src/pyinfra/operations/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ def volume(volume: str, driver: str = "", labels: list[str] | None = None, prese
existent_volume = host.get_fact(DockerVolume, object_id=volume)

if present:

if existent_volume:
host.noop("Volume already exists!")
return
Expand Down
7 changes: 2 additions & 5 deletions src/pyinfra/operations/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,7 @@ def download(

if md5sum:
yield make_formatted_string_command(
(
"(( md5sum {0} 2> /dev/null || md5 {0} ) | grep {1}) "
"|| ( echo {2} && exit 1 )"
),
("(( md5sum {0} 2> /dev/null || md5 {0} ) | grep {1}) || ( echo {2} && exit 1 )"),
QuoteString(dest),
md5sum,
QuoteString("MD5 did not match!"),
Expand Down Expand Up @@ -1694,7 +1691,7 @@ def flags(path: str, flags: list[str] | None = None, present=True):
yield StringCommand("chflags", new_flags, QuoteString(path))
else:
host.noop(
f'\'{path}\' already has \'{",".join(flags)}\' {"set" if present else "clear"}',
f"'{path}' already has '{','.join(flags)}' {'set' if present else 'clear'}",
)


Expand Down
2 changes: 1 addition & 1 deletion src/pyinfra/operations/iptables.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def rule(
# --log-prefix is only supported with jump=LOG
if log_prefix and jump != "LOG":
raise OperationError(
"iptables only supports log_prefix with the LOG jump " "(jump={0})".format(jump),
"iptables only supports log_prefix with the LOG jump (jump={0})".format(jump),
)

definition = {
Expand Down
2 changes: 1 addition & 1 deletion src/pyinfra/operations/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def privileges(

def handle_privileges(action, target, privileges_to_apply, with_statement=None):
command = (
"{action} {privileges} " "ON {database}.{table} " '{target} "{user}"@"{user_hostname}"'
'{action} {privileges} ON {database}.{table} {target} "{user}"@"{user_hostname}"'
).format(
privileges=", ".join(sorted(privileges_to_apply)),
action=action,
Expand Down
2 changes: 1 addition & 1 deletion src/pyinfra/operations/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ def service(

else:
raise OperationError(
("No init system found " "(no systemctl, initctl, /etc/init.d or /etc/rc.d found)"),
("No init system found (no systemctl, initctl, /etc/init.d or /etc/rc.d found)"),
)

yield from service_operation._inner(
Expand Down
12 changes: 8 additions & 4 deletions src/pyinfra/operations/util/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ def get_timestamp() -> str:

def _sed_delete_builder(line: str, replace: str, flags: str, interpolate_variables: bool) -> str:
return (
'"/{0}/{1}d"' if interpolate_variables else # fmt: skip
"'/{0}/{1}d'"
'"/{0}/{1}d"'
if interpolate_variables
# fmt: skip
else "'/{0}/{1}d'"
).format(line, "I" if _sed_ignore_case.search(flags) else "")


Expand All @@ -65,8 +67,10 @@ def sed_delete(

def _sed_replace_builder(line: str, replace: str, flags: str, interpolate_variables: bool) -> str:
return (
'"s/{0}/{1}/{2}"' if interpolate_variables else # fmt: skip
"'s/{0}/{1}/{2}'"
'"s/{0}/{1}/{2}"'
if interpolate_variables
# fmt: skip
else "'s/{0}/{1}/{2}'"
).format(line, replace, flags)


Expand Down
1 change: 0 additions & 1 deletion src/pyinfra_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,6 @@ def _prepare_exec_operations(state, config, operations):


def _prepare_deploy_operations(state, config, operations):

# Number of "steps" to make = number of files * number of hosts
for i, filename in enumerate(operations):
_log_styled_msg = click.style(filename, bold=True)
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Import `pyinfra_cli` to trigger gevent monkey patching
import logging # noqa: I100
import logging

import gevent.hub

Expand Down
Loading