pyinfra 2.9.2__py2.py3-none-any.whl → 3.0b1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +261 -255
  3. pyinfra/api/arguments_typed.py +77 -0
  4. pyinfra/api/command.py +66 -53
  5. pyinfra/api/config.py +27 -22
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +2 -24
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +77 -113
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +17 -25
  13. pyinfra/api/operation.py +232 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +55 -70
  17. pyinfra/connectors/base.py +150 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +227 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +195 -207
  22. pyinfra/connectors/ssh.py +528 -615
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +212 -137
  27. pyinfra/connectors/vagrant.py +55 -48
  28. pyinfra/context.py +3 -2
  29. pyinfra/facts/docker.py +1 -0
  30. pyinfra/facts/files.py +45 -32
  31. pyinfra/facts/git.py +3 -1
  32. pyinfra/facts/gpg.py +1 -1
  33. pyinfra/facts/hardware.py +4 -2
  34. pyinfra/facts/iptables.py +5 -3
  35. pyinfra/facts/mysql.py +1 -0
  36. pyinfra/facts/postgres.py +168 -0
  37. pyinfra/facts/postgresql.py +5 -161
  38. pyinfra/facts/selinux.py +3 -1
  39. pyinfra/facts/server.py +77 -30
  40. pyinfra/facts/systemd.py +29 -12
  41. pyinfra/facts/sysvinit.py +10 -10
  42. pyinfra/facts/util/packaging.py +4 -2
  43. pyinfra/local.py +4 -5
  44. pyinfra/operations/apk.py +3 -3
  45. pyinfra/operations/apt.py +25 -47
  46. pyinfra/operations/brew.py +7 -14
  47. pyinfra/operations/bsdinit.py +4 -4
  48. pyinfra/operations/cargo.py +1 -1
  49. pyinfra/operations/choco.py +1 -1
  50. pyinfra/operations/dnf.py +4 -4
  51. pyinfra/operations/files.py +108 -321
  52. pyinfra/operations/gem.py +1 -1
  53. pyinfra/operations/git.py +6 -37
  54. pyinfra/operations/iptables.py +2 -10
  55. pyinfra/operations/launchd.py +1 -1
  56. pyinfra/operations/lxd.py +1 -9
  57. pyinfra/operations/mysql.py +5 -28
  58. pyinfra/operations/npm.py +1 -1
  59. pyinfra/operations/openrc.py +1 -1
  60. pyinfra/operations/pacman.py +3 -3
  61. pyinfra/operations/pip.py +14 -15
  62. pyinfra/operations/pkg.py +1 -1
  63. pyinfra/operations/pkgin.py +3 -3
  64. pyinfra/operations/postgres.py +347 -0
  65. pyinfra/operations/postgresql.py +17 -380
  66. pyinfra/operations/python.py +2 -17
  67. pyinfra/operations/selinux.py +5 -28
  68. pyinfra/operations/server.py +59 -84
  69. pyinfra/operations/snap.py +1 -3
  70. pyinfra/operations/ssh.py +8 -23
  71. pyinfra/operations/systemd.py +7 -7
  72. pyinfra/operations/sysvinit.py +3 -12
  73. pyinfra/operations/upstart.py +4 -4
  74. pyinfra/operations/util/__init__.py +12 -0
  75. pyinfra/operations/util/files.py +2 -2
  76. pyinfra/operations/util/packaging.py +6 -24
  77. pyinfra/operations/util/service.py +18 -37
  78. pyinfra/operations/vzctl.py +2 -2
  79. pyinfra/operations/xbps.py +3 -3
  80. pyinfra/operations/yum.py +4 -4
  81. pyinfra/operations/zypper.py +4 -4
  82. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
  83. pyinfra-3.0b1.dist-info/RECORD +163 -0
  84. pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
  85. pyinfra_cli/__main__.py +2 -0
  86. pyinfra_cli/commands.py +7 -2
  87. pyinfra_cli/exceptions.py +83 -42
  88. pyinfra_cli/inventory.py +19 -4
  89. pyinfra_cli/log.py +17 -3
  90. pyinfra_cli/main.py +133 -90
  91. pyinfra_cli/prints.py +93 -129
  92. pyinfra_cli/util.py +60 -29
  93. tests/test_api/test_api.py +2 -0
  94. tests/test_api/test_api_arguments.py +13 -13
  95. tests/test_api/test_api_deploys.py +28 -29
  96. tests/test_api/test_api_facts.py +60 -98
  97. tests/test_api/test_api_operations.py +100 -200
  98. tests/test_cli/test_cli.py +18 -49
  99. tests/test_cli/test_cli_deploy.py +11 -37
  100. tests/test_cli/test_cli_exceptions.py +50 -19
  101. tests/test_cli/util.py +1 -1
  102. tests/test_connectors/test_chroot.py +6 -6
  103. tests/test_connectors/test_docker.py +4 -4
  104. tests/test_connectors/test_dockerssh.py +38 -50
  105. tests/test_connectors/test_local.py +11 -12
  106. tests/test_connectors/test_ssh.py +66 -107
  107. tests/test_connectors/test_terraform.py +9 -15
  108. tests/test_connectors/test_util.py +24 -46
  109. tests/test_connectors/test_vagrant.py +4 -4
  110. pyinfra/api/operation.pyi +0 -117
  111. pyinfra/connectors/ansible.py +0 -171
  112. pyinfra/connectors/mech.py +0 -186
  113. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  114. pyinfra/connectors/winrm.py +0 -320
  115. pyinfra/facts/windows.py +0 -366
  116. pyinfra/facts/windows_files.py +0 -90
  117. pyinfra/operations/windows.py +0 -59
  118. pyinfra/operations/windows_files.py +0 -551
  119. pyinfra-2.9.2.dist-info/RECORD +0 -170
  120. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  121. tests/test_connectors/test_ansible.py +0 -64
  122. tests/test_connectors/test_mech.py +0 -126
  123. tests/test_connectors/test_winrm.py +0 -76
  124. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
  125. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
  126. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/operations/gem.py CHANGED
@@ -9,7 +9,7 @@ from pyinfra.facts.gem import GemPackages
9
9
  from .util.packaging import ensure_packages
10
10
 
11
11
 
12
- @operation
12
+ @operation()
13
13
  def packages(packages=None, present=True, latest=False):
14
14
  """
15
15
  Add/remove/update gem packages.
pyinfra/operations/git.py CHANGED
@@ -13,9 +13,7 @@ from . import files, ssh
13
13
  from .util.files import chown, unix_path_join
14
14
 
15
15
 
16
- @operation(
17
- pipeline_facts={"git_config": "repo"},
18
- )
16
+ @operation()
19
17
  def config(key, value, multi_value=False, repo=None):
20
18
  """
21
19
  Manage git config for a repository or globally.
@@ -54,19 +52,15 @@ def config(key, value, multi_value=False, repo=None):
54
52
 
55
53
  if not multi_value and existing_config.get(key) != [value]:
56
54
  yield '{0} {1} "{2}"'.format(base_command, key, value)
57
- existing_config[key] = [value]
58
55
 
59
56
  elif multi_value and value not in existing_config.get(key, []):
60
57
  yield '{0} --add {1} "{2}"'.format(base_command, key, value)
61
- existing_config.setdefault(key, []).append(value)
62
58
 
63
59
  else:
64
60
  host.noop("git config {0} is set to {1}".format(key, value))
65
61
 
66
62
 
67
- @operation(
68
- pipeline_facts={"git_branch": "target"},
69
- )
63
+ @operation()
70
64
  def repo(
71
65
  src,
72
66
  dest,
@@ -105,7 +99,7 @@ def repo(
105
99
  """
106
100
 
107
101
  # Ensure our target directory exists
108
- yield from files.directory(dest)
102
+ yield from files.directory._inner(dest)
109
103
 
110
104
  # Do we need to scan for the remote host key?
111
105
  if ssh_keyscan:
@@ -113,7 +107,7 @@ def repo(
113
107
  domain = re.match(r"^[a-zA-Z0-9]+@([0-9a-zA-Z\.\-]+)", src)
114
108
 
115
109
  if domain:
116
- yield from ssh.keyscan(domain.group(1))
110
+ yield from ssh.keyscan._inner(domain.group(1))
117
111
  else:
118
112
  raise OperationError(
119
113
  "Could not parse domain (to SSH keyscan) from: {0}".format(src),
@@ -130,21 +124,11 @@ def repo(
130
124
  git_commands.append("clone {0} --branch {1} .".format(src, branch))
131
125
  else:
132
126
  git_commands.append("clone {0} .".format(src))
133
-
134
- host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
135
- host.create_fact(
136
- Directory,
137
- kwargs={"path": git_dir},
138
- data={"user": user, "group": group},
139
- )
140
-
141
127
  # Ensuring existing repo
142
128
  else:
143
129
  if branch and host.get_fact(GitBranch, repo=dest) != branch:
144
130
  git_commands.append("fetch") # fetch to ensure we have the branch locally
145
131
  git_commands.append("checkout {0}".format(branch))
146
- host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
147
-
148
132
  if pull:
149
133
  if rebase:
150
134
  git_commands.append("pull --rebase")
@@ -293,7 +277,6 @@ def worktree(
293
277
 
294
278
  # Doesn't exist & we want it
295
279
  if not host.get_fact(Directory, path=worktree) and present:
296
-
297
280
  # be sure that `repo` is a GIT repository
298
281
  if not assume_repo_exists and not host.get_fact(
299
282
  Directory,
@@ -321,12 +304,8 @@ def worktree(
321
304
  if user or group:
322
305
  yield chown(worktree, user, group, recursive=True)
323
306
 
324
- host.create_fact(Directory, kwargs={"path": worktree}, data={"user": user, "group": group})
325
- host.create_fact(GitTrackingBranch, kwargs={"repo": worktree}, data=new_branch)
326
-
327
307
  # It exists and we don't want it
328
308
  elif host.get_fact(Directory, path=worktree) and not present:
329
-
330
309
  command = "cd {0} && git worktree remove .".format(worktree)
331
310
 
332
311
  if force:
@@ -334,12 +313,8 @@ def worktree(
334
313
 
335
314
  yield command
336
315
 
337
- host.delete_fact(Directory, kwargs={"path": worktree})
338
- host.create_fact(GitTrackingBranch, kwargs={"repo": worktree})
339
-
340
316
  # It exists and we still want it => pull/rebase it
341
317
  elif host.get_fact(Directory, path=worktree) and present:
342
-
343
318
  # pull the worktree only if it's already linked to a tracking branch or
344
319
  # if a remote branch is set
345
320
  if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
@@ -359,7 +334,7 @@ def worktree(
359
334
  yield command
360
335
 
361
336
 
362
- @operation
337
+ @operation()
363
338
  def bare_repo(
364
339
  path,
365
340
  user=None,
@@ -384,7 +359,7 @@ def bare_repo(
384
359
  )
385
360
  """
386
361
 
387
- yield from files.directory(path, present=present)
362
+ yield from files.directory._inner(path, present=present)
388
363
 
389
364
  if present:
390
365
  head_filename = unix_path_join(path, "HEAD")
@@ -397,9 +372,3 @@ def bare_repo(
397
372
  else:
398
373
  if (user and head_file["user"] != user) or (group and head_file["group"] != group):
399
374
  yield chown(path, user, group, recursive=True)
400
-
401
- host.create_fact(
402
- File,
403
- kwargs={"path": head_filename},
404
- data={"user": user, "group": group, "mode": None},
405
- )
@@ -8,7 +8,7 @@ from pyinfra.api.exceptions import OperationError
8
8
  from pyinfra.facts.iptables import Ip6tablesChains, Ip6tablesRules, IptablesChains, IptablesRules
9
9
 
10
10
 
11
- @operation
11
+ @operation()
12
12
  def chain(
13
13
  chain,
14
14
  present=True,
@@ -41,7 +41,6 @@ def chain(
41
41
  if not present:
42
42
  if chain in chains:
43
43
  yield "{0} -X {1}".format(command, chain)
44
- chains.pop(chain)
45
44
  else:
46
45
  host.noop("iptables chain {0} does not exist".format(chain))
47
46
  return
@@ -49,17 +48,15 @@ def chain(
49
48
  if present:
50
49
  if chain not in chains:
51
50
  yield "{0} -N {1}".format(command, chain)
52
- chains[chain] = None # policy will be set below
53
51
  else:
54
52
  host.noop("iptables chain {0} exists".format(chain))
55
53
 
56
54
  if policy:
57
55
  if chain not in chains or chains[chain] != policy:
58
56
  yield "{0} -P {1} {2}".format(command, chain, policy)
59
- chains[chain] = policy
60
57
 
61
58
 
62
- @operation
59
+ @operation()
63
60
  def rule(
64
61
  chain,
65
62
  jump,
@@ -310,8 +307,3 @@ def rule(
310
307
 
311
308
  # Build the final iptables command
312
309
  yield " ".join(args)
313
-
314
- if action == "-D":
315
- rules.remove(definition)
316
- else:
317
- rules.append(definition)
@@ -9,7 +9,7 @@ from pyinfra.facts.launchd import LaunchdStatus
9
9
  from .util.service import handle_service_control
10
10
 
11
11
 
12
- @operation
12
+ @operation()
13
13
  def service(
14
14
  service,
15
15
  running=True,
pyinfra/operations/lxd.py CHANGED
@@ -15,7 +15,7 @@ def get_container_named(name, containers):
15
15
  return None
16
16
 
17
17
 
18
- @operation
18
+ @operation()
19
19
  def container(
20
20
  id,
21
21
  present=True,
@@ -53,8 +53,6 @@ def container(
53
53
 
54
54
  # Command to remove the container:
55
55
  yield "lxc delete {0}".format(id)
56
-
57
- current_containers.remove(container)
58
56
  else:
59
57
  host.noop("container {0} does not exist".format(id))
60
58
 
@@ -63,11 +61,5 @@ def container(
63
61
  if not container:
64
62
  # Command to create the container:
65
63
  yield "lxc launch {image} {id} < /dev/null".format(id=id, image=image)
66
- current_containers.append(
67
- {
68
- "name": id,
69
- "image": image,
70
- },
71
- )
72
64
  else:
73
65
  host.noop("container {0} exists".format(id))
@@ -52,7 +52,7 @@ def sql(
52
52
  )
53
53
 
54
54
 
55
- @operation
55
+ @operation()
56
56
  def user(
57
57
  user,
58
58
  present=True,
@@ -162,22 +162,10 @@ def user(
162
162
  host=mysql_host,
163
163
  port=mysql_port,
164
164
  )
165
- current_users.pop(user_host)
166
165
  else:
167
166
  host.noop("mysql user {0}@{1} does not exist".format(user, user_hostname))
168
167
  return
169
168
 
170
- new_or_updated_user_fact = {
171
- "ssl_type": "ANY" if require == "SSL" else require,
172
- "ssl_cipher": require_cipher,
173
- "x509_issuer": require_issuer,
174
- "x509_subject": require_subject,
175
- "max_user_connections": max_connections,
176
- "max_questions": max_queries_per_hour,
177
- "max_updates": max_updates_per_hour,
178
- "max_connections": max_connections_per_hour,
179
- }
180
-
181
169
  if present and not is_present:
182
170
  sql_bits = ['CREATE USER "{0}"@"{1}"'.format(user, user_hostname)]
183
171
  if password:
@@ -224,8 +212,6 @@ def user(
224
212
  port=mysql_port,
225
213
  )
226
214
 
227
- current_users[user_host] = new_or_updated_user_fact
228
-
229
215
  if present and is_present:
230
216
  current_user = current_users.get(user_host)
231
217
 
@@ -277,14 +263,13 @@ def user(
277
263
  host=mysql_host,
278
264
  port=mysql_port,
279
265
  )
280
- current_user.update(new_or_updated_user_fact)
281
266
  else:
282
267
  host.noop("mysql user {0}@{1} exists".format(user, user_hostname))
283
268
 
284
269
  # If we're here either the user exists or we just created them; either way
285
270
  # now we can check any privileges are set.
286
271
  if privileges:
287
- yield from _privileges(
272
+ yield from _privileges._inner(
288
273
  user,
289
274
  privileges,
290
275
  user_hostname=user_hostname,
@@ -295,7 +280,7 @@ def user(
295
280
  )
296
281
 
297
282
 
298
- @operation
283
+ @operation()
299
284
  def database(
300
285
  database,
301
286
  # Desired database settings
@@ -359,7 +344,6 @@ def database(
359
344
  host=mysql_host,
360
345
  port=mysql_port,
361
346
  )
362
- current_databases.pop(database)
363
347
  else:
364
348
  host.noop("mysql database {0} does not exist".format(database))
365
349
  return
@@ -381,16 +365,12 @@ def database(
381
365
  host=mysql_host,
382
366
  port=mysql_port,
383
367
  )
384
- current_databases[database] = {
385
- "collate": collate,
386
- "charset": charset,
387
- }
388
368
  else:
389
369
  host.noop("mysql database {0} exists".format(database))
390
370
 
391
371
  # Ensure any user privileges for this database
392
372
  if user and user_privileges:
393
- yield from privileges(
373
+ yield from privileges._inner(
394
374
  user,
395
375
  user_hostname=user_hostname,
396
376
  privileges=user_privileges,
@@ -402,7 +382,7 @@ def database(
402
382
  )
403
383
 
404
384
 
405
- @operation
385
+ @operation()
406
386
  def privileges(
407
387
  user,
408
388
  privileges,
@@ -504,9 +484,6 @@ def privileges(
504
484
  # Find / grant any privileges that we want but do not exist
505
485
  privileges_to_grant = privileges - existing_privileges
506
486
 
507
- user_grants[database_table] -= privileges_to_revoke
508
- user_grants[database_table].update(privileges_to_grant)
509
-
510
487
  if privileges_to_grant:
511
488
  # We will grant something on this table, no need to revoke "USAGE" as it will be overridden
512
489
  privileges_to_revoke.discard("USAGE")
pyinfra/operations/npm.py CHANGED
@@ -9,7 +9,7 @@ from pyinfra.facts.npm import NpmPackages
9
9
  from .util.packaging import ensure_packages
10
10
 
11
11
 
12
- @operation
12
+ @operation()
13
13
  def packages(packages=None, present=True, latest=False, directory=None):
14
14
  """
15
15
  Install/remove/update npm packages.
@@ -9,7 +9,7 @@ from pyinfra.facts.openrc import OpenrcEnabled, OpenrcStatus
9
9
  from .util.service import handle_service_control
10
10
 
11
11
 
12
- @operation
12
+ @operation()
13
13
  def service(
14
14
  service,
15
15
  running=True,
@@ -18,7 +18,7 @@ def upgrade():
18
18
  yield "pacman --noconfirm -Su"
19
19
 
20
20
 
21
- _upgrade = upgrade # noqa: E305
21
+ _upgrade = upgrade._inner # noqa: E305
22
22
 
23
23
 
24
24
  @operation(is_idempotent=False)
@@ -30,10 +30,10 @@ def update():
30
30
  yield "pacman -Sy"
31
31
 
32
32
 
33
- _update = update # noqa: E305
33
+ _update = update._inner # noqa: E305
34
34
 
35
35
 
36
- @operation
36
+ @operation()
37
37
  def packages(
38
38
  packages=None,
39
39
  present=True,
pyinfra/operations/pip.py CHANGED
@@ -12,7 +12,7 @@ from . import files
12
12
  from .util.packaging import ensure_packages
13
13
 
14
14
 
15
- @operation
15
+ @operation()
16
16
  def virtualenv(
17
17
  path,
18
18
  python=None,
@@ -46,7 +46,7 @@ def virtualenv(
46
46
 
47
47
  if present is False:
48
48
  if host.get_fact(File, path=activate_script_path):
49
- yield from files.directory(path, present=False)
49
+ yield from files.directory._inner(path, present=False)
50
50
  else:
51
51
  host.noop("virtualenv {0} does not exist".format(path))
52
52
 
@@ -72,20 +72,14 @@ def virtualenv(
72
72
  command.append(path)
73
73
 
74
74
  yield " ".join(command)
75
-
76
- host.create_fact(
77
- File,
78
- kwargs={"path": activate_script_path},
79
- data={"user": None, "group": None},
80
- )
81
75
  else:
82
76
  host.noop("virtualenv {0} exists".format(path))
83
77
 
84
78
 
85
- _virtualenv = virtualenv # noqa
79
+ _virtualenv = virtualenv._inner # noqa
86
80
 
87
81
 
88
- @operation
82
+ @operation()
89
83
  def venv(
90
84
  path,
91
85
  python=None,
@@ -111,7 +105,7 @@ def venv(
111
105
  )
112
106
  """
113
107
 
114
- yield from virtualenv(
108
+ yield from _virtualenv(
115
109
  venv=True,
116
110
  path=path,
117
111
  python=python,
@@ -121,7 +115,7 @@ def venv(
121
115
  )
122
116
 
123
117
 
124
- @operation
118
+ @operation()
125
119
  def packages(
126
120
  packages=None,
127
121
  present=True,
@@ -173,10 +167,10 @@ def packages(
173
167
  virtualenv = virtualenv.rstrip("/")
174
168
  pip = "{0}/bin/{1}".format(virtualenv, pip)
175
169
 
176
- install_command = [pip, "install"]
170
+ install_command_args = [pip, "install"]
177
171
  if extra_install_args:
178
- install_command.append(extra_install_args)
179
- install_command = " ".join(install_command)
172
+ install_command_args.append(extra_install_args)
173
+ install_command = " ".join(install_command_args)
180
174
 
181
175
  uninstall_command = " ".join([pip, "uninstall", "--yes"])
182
176
 
@@ -191,6 +185,11 @@ def packages(
191
185
  if packages:
192
186
  current_packages = host.get_fact(PipPackages, pip=pip)
193
187
 
188
+ # PEP-0426 states that Python packages should be compared using lowercase, so lowercase both
189
+ # the input packages and the fact packages before comparison.
190
+ packages = [pkg.lower() for pkg in packages]
191
+ current_packages = {pkg.lower(): versions for pkg, versions in current_packages.items()}
192
+
194
193
  yield from ensure_packages(
195
194
  host,
196
195
  packages,
pyinfra/operations/pkg.py CHANGED
@@ -11,7 +11,7 @@ from pyinfra.facts.server import Arch, Os, OsVersion, Which
11
11
  from .util.packaging import ensure_packages
12
12
 
13
13
 
14
- @operation
14
+ @operation()
15
15
  def packages(packages=None, present=True, pkg_path=None):
16
16
  """
17
17
  Install/remove/update pkg packages. This will use ``pkg ...`` where available
@@ -18,7 +18,7 @@ def upgrade():
18
18
  yield "pkgin -y upgrade"
19
19
 
20
20
 
21
- _upgrade = upgrade # noqa: E305
21
+ _upgrade = upgrade._inner # noqa: E305
22
22
 
23
23
 
24
24
  @operation(is_idempotent=False)
@@ -30,10 +30,10 @@ def update():
30
30
  yield "pkgin -y update"
31
31
 
32
32
 
33
- _update = update # noqa: E305
33
+ _update = update._inner # noqa: E305
34
34
 
35
35
 
36
- @operation
36
+ @operation()
37
37
  def packages(
38
38
  packages=None,
39
39
  present=True,