cfengine 0.11.2__tar.gz → 0.12.1__tar.gz

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 (65) hide show
  1. {cfengine-0.11.2 → cfengine-0.12.1}/PKG-INFO +1 -1
  2. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/PKG-INFO +1 -1
  3. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/SOURCES.txt +1 -0
  4. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/commands.py +6 -7
  5. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/docs.py +1 -1
  6. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/lint.py +165 -33
  7. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/main.py +7 -1
  8. cfengine-0.12.1/src/cfengine_cli/policy_language.py +239 -0
  9. {cfengine-0.11.2 → cfengine-0.12.1}/uv.lock +3 -3
  10. {cfengine-0.11.2 → cfengine-0.12.1}/.github/dependabot.yml +0 -0
  11. {cfengine-0.11.2 → cfengine-0.12.1}/.github/workflows/format.yml +0 -0
  12. {cfengine-0.11.2 → cfengine-0.12.1}/.github/workflows/lint.yml +0 -0
  13. {cfengine-0.11.2 → cfengine-0.12.1}/.github/workflows/pypi-publish.yml +0 -0
  14. {cfengine-0.11.2 → cfengine-0.12.1}/.github/workflows/test.yml +0 -0
  15. {cfengine-0.11.2 → cfengine-0.12.1}/.gitignore +0 -0
  16. {cfengine-0.11.2 → cfengine-0.12.1}/.python-version +0 -0
  17. {cfengine-0.11.2 → cfengine-0.12.1}/HACKING.md +0 -0
  18. {cfengine-0.11.2 → cfengine-0.12.1}/LICENSE +0 -0
  19. {cfengine-0.11.2 → cfengine-0.12.1}/README.md +0 -0
  20. {cfengine-0.11.2 → cfengine-0.12.1}/ci/01-install.sh +0 -0
  21. {cfengine-0.11.2 → cfengine-0.12.1}/ci/02-safe-tests.sh +0 -0
  22. {cfengine-0.11.2 → cfengine-0.12.1}/ci/03-unsafe-tests.sh +0 -0
  23. {cfengine-0.11.2 → cfengine-0.12.1}/pyproject.toml +0 -0
  24. {cfengine-0.11.2 → cfengine-0.12.1}/setup.cfg +0 -0
  25. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/dependency_links.txt +0 -0
  26. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/entry_points.txt +0 -0
  27. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/requires.txt +0 -0
  28. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine.egg-info/top_level.txt +0 -0
  29. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/__init__.py +0 -0
  30. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/__main__.py +0 -0
  31. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/deptool-README.md +0 -0
  32. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/deptool.py +0 -0
  33. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/dev.py +0 -0
  34. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/format.py +0 -0
  35. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/__init__.py +0 -0
  36. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/analyze.py +0 -0
  37. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/check_download_matches_git.py +0 -0
  38. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/download.py +0 -0
  39. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/generate_git_tags.py +0 -0
  40. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/generate_release_information.py +0 -0
  41. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/generate_vcf_download.py +0 -0
  42. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/masterfiles/generate_vcf_git_checkout.py +0 -0
  43. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/paths.py +0 -0
  44. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/profile.py +0 -0
  45. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/shell.py +0 -0
  46. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/utils.py +0 -0
  47. {cfengine-0.11.2 → cfengine-0.12.1}/src/cfengine_cli/version.py +0 -0
  48. {cfengine-0.11.2 → cfengine-0.12.1}/tests/__init__.py +0 -0
  49. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/001_hello_world.expected.cf +0 -0
  50. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/001_hello_world.input.cf +0 -0
  51. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/002_basics.expected.cf +0 -0
  52. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/002_basics.input.cf +0 -0
  53. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/003_wrapping.expected.cf +0 -0
  54. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/003_wrapping.input.cf +0 -0
  55. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/004_comments.expected.cf +0 -0
  56. {cfengine-0.11.2 → cfengine-0.12.1}/tests/format/004_comments.input.cf +0 -0
  57. {cfengine-0.11.2 → cfengine-0.12.1}/tests/run-format-tests.sh +0 -0
  58. {cfengine-0.11.2 → cfengine-0.12.1}/tests/run-shell-tests.sh +0 -0
  59. {cfengine-0.11.2 → cfengine-0.12.1}/tests/shell/001-help.sh +0 -0
  60. {cfengine-0.11.2 → cfengine-0.12.1}/tests/shell/002-version.sh +0 -0
  61. {cfengine-0.11.2 → cfengine-0.12.1}/tests/shell/003-format.sh +0 -0
  62. {cfengine-0.11.2 → cfengine-0.12.1}/tests/test_deps.py +0 -0
  63. {cfengine-0.11.2 → cfengine-0.12.1}/tests/test_paths.py +0 -0
  64. {cfengine-0.11.2 → cfengine-0.12.1}/tests/test_utils.py +0 -0
  65. {cfengine-0.11.2 → cfengine-0.12.1}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.11.2
3
+ Version: 0.12.1
4
4
  Summary: Human-oriented CLI for interacting with CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cfengine
3
- Version: 0.11.2
3
+ Version: 0.12.1
4
4
  Summary: Human-oriented CLI for interacting with CFEngine tools
5
5
  License: GNU GENERAL PUBLIC LICENSE
6
6
  Version 3, 29 June 2007
@@ -30,6 +30,7 @@ src/cfengine_cli/format.py
30
30
  src/cfengine_cli/lint.py
31
31
  src/cfengine_cli/main.py
32
32
  src/cfengine_cli/paths.py
33
+ src/cfengine_cli/policy_language.py
33
34
  src/cfengine_cli/profile.py
34
35
  src/cfengine_cli/shell.py
35
36
  src/cfengine_cli/utils.py
@@ -4,7 +4,7 @@ import re
4
4
  import json
5
5
  from cfengine_cli.profile import profile_cfengine, generate_callstack
6
6
  from cfengine_cli.dev import dispatch_dev_subcommand
7
- from cfengine_cli.lint import lint_single_arg, lint_folder
7
+ from cfengine_cli.lint import lint_folder, lint_single_arg
8
8
  from cfengine_cli.shell import user_command
9
9
  from cfengine_cli.paths import bin
10
10
  from cfengine_cli.version import cfengine_cli_version_string
@@ -94,21 +94,20 @@ def format(names, line_length) -> int:
94
94
  return 0
95
95
 
96
96
 
97
- def _lint(files) -> int:
98
-
97
+ def _lint(files, strict) -> int:
99
98
  if not files:
100
- return lint_folder(".")
99
+ return lint_folder(".", strict)
101
100
 
102
101
  errors = 0
103
102
 
104
103
  for file in files:
105
- errors += lint_single_arg(file)
104
+ errors += lint_single_arg(file, strict)
106
105
 
107
106
  return errors
108
107
 
109
108
 
110
- def lint(files) -> int:
111
- errors = _lint(files)
109
+ def lint(files, strict) -> int:
110
+ errors = _lint(files, strict)
112
111
  if errors == 0:
113
112
  print("Success, no errors found.")
114
113
  else:
@@ -409,7 +409,7 @@ def check_docs() -> int:
409
409
 
410
410
  Run by the command:
411
411
  cfengine dev lint-docs"""
412
- r = lint_folder(".")
412
+ r = lint_folder(".", strict=False)
413
413
  if r != 0:
414
414
  return r
415
415
  _process_markdown_code_blocks(
@@ -14,13 +14,50 @@ import os
14
14
  import json
15
15
  import itertools
16
16
  import tree_sitter_cfengine as tscfengine
17
+ from dataclasses import dataclass
17
18
  from tree_sitter import Language, Parser
18
19
  from cfbs.validate import validate_config
19
20
  from cfbs.cfbs_config import CFBSConfig
20
21
  from cfbs.utils import find
21
-
22
- DEPRECATED_PROMISE_TYPES = ["defaults", "guest_environments"]
23
- ALLOWED_BUNDLE_TYPES = ["agent", "common", "monitor", "server", "edit_line", "edit_xml"]
22
+ from cfengine_cli.policy_language import (
23
+ DEPRECATED_PROMISE_TYPES,
24
+ ALLOWED_BUNDLE_TYPES,
25
+ BUILTIN_PROMISE_TYPES,
26
+ BUILTIN_FUNCTIONS,
27
+ )
28
+
29
+
30
+ @dataclass
31
+ class _State:
32
+ block_type: str | None = None # "bundle" | "body" | "promise" | None
33
+ promise_type: str | None = None # "vars" | "files" | "classes" | ... | None
34
+ attribute_name: str | None = None # "if" | "string" | "slist" | ... | None
35
+
36
+ def update(self, node) -> "_State":
37
+ """Updates and returns the state that should apply to the children of `node`."""
38
+ if node.type == "bundle_block":
39
+ return _State(block_type="bundle")
40
+ if node.type == "body_block":
41
+ return _State(block_type="body")
42
+ if node.type == "promise_block":
43
+ return _State(block_type="promise")
44
+ if node.type == "bundle_section":
45
+ for child in node.children:
46
+ if child.type == "promise_guard":
47
+ return _State(
48
+ block_type=self.block_type,
49
+ promise_type=_text(child)[:-1], # strip trailing ':'
50
+ )
51
+ return _State(block_type=self.block_type)
52
+ if node.type == "attribute":
53
+ for child in node.children:
54
+ if child.type == "attribute_name":
55
+ return _State(
56
+ block_type=self.block_type,
57
+ promise_type=self.promise_type,
58
+ attribute_name=_text(child),
59
+ )
60
+ return self
24
61
 
25
62
 
26
63
  def lint_cfbs_json(filename) -> int:
@@ -90,16 +127,9 @@ def _find_node_type(filename, lines, node, node_type):
90
127
  return matches
91
128
 
92
129
 
93
- def _find_nodes(filename, lines, node):
94
- matches = []
95
- visitor = lambda x: matches.append(x)
96
- _walk_generic(filename, lines, node, visitor)
97
- return matches
98
-
99
-
100
- def _single_node_checks(filename, lines, node):
101
- """Things which can be checked by only looking at one node,
102
- not needing to recurse into children."""
130
+ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
131
+ """Checks we run on each node in the syntax tree,
132
+ utilizes state for checks which require context."""
103
133
  line = node.range.start_point[0] + 1
104
134
  column = node.range.start_point[1] + 1
105
135
  if node.type == "attribute_name" and _text(node) == "ifvarclass":
@@ -117,6 +147,19 @@ def _single_node_checks(filename, lines, node):
117
147
  f"Deprecation: Promise type '{promise_type}' is deprecated at {filename}:{line}:{column}"
118
148
  )
119
149
  return 1
150
+ if strict and (
151
+ (
152
+ promise_type
153
+ not in BUILTIN_PROMISE_TYPES.union(
154
+ user_definition.get("custom_promise_types", set())
155
+ )
156
+ )
157
+ ):
158
+ _highlight_range(node, lines)
159
+ print(
160
+ f"Error: Undefined promise type '{promise_type}' at {filename}:{line}:{column}"
161
+ )
162
+ return 1
120
163
  if node.type == "bundle_block_name":
121
164
  if _text(node) != _text(node).lower():
122
165
  _highlight_range(node, lines)
@@ -138,10 +181,52 @@ def _single_node_checks(filename, lines, node):
138
181
  f"Error: Bundle type must be one of ({', '.join(ALLOWED_BUNDLE_TYPES)}), not '{_text(node)}' at {filename}:{line}:{column}"
139
182
  )
140
183
  return 1
184
+ if node.type == "calling_identifier":
185
+ if (
186
+ strict
187
+ and _text(node) in user_definition.get("all_bundle_names", set())
188
+ and state.promise_type in user_definition.get("custom_promise_types", set())
189
+ ):
190
+ _highlight_range(node, lines)
191
+ print(
192
+ f"Error: Call to bundle '{_text(node)}' inside custom promise: '{state.promise_type}' at {filename}:{line}:{column}"
193
+ )
194
+ return 1
195
+ if strict and (
196
+ _text(node)
197
+ not in BUILTIN_FUNCTIONS.union(
198
+ user_definition.get("all_bundle_names", set()),
199
+ user_definition.get("all_body_names", set()),
200
+ )
201
+ ):
202
+ _highlight_range(node, lines)
203
+ print(
204
+ f"Error: Call to unknown function / bundle / body '{_text(node)}' at at {filename}:{line}:{column}"
205
+ )
206
+ return 1
141
207
  return 0
142
208
 
143
209
 
144
- def _walk(filename, lines, node) -> int:
210
+ def _stateful_walk(
211
+ filename, lines, node, user_definition, strict, state: _State | None = None
212
+ ) -> int:
213
+ if state is None:
214
+ state = _State()
215
+
216
+ errors = _node_checks(filename, lines, node, user_definition, strict, state)
217
+
218
+ child_state = state.update(node)
219
+ for child in node.children:
220
+ errors += _stateful_walk(
221
+ filename, lines, child, user_definition, strict, child_state
222
+ )
223
+ return errors
224
+
225
+
226
+ def _walk(filename, lines, node, user_definition=None, strict=True) -> int:
227
+ if user_definition is None:
228
+ user_definition = {}
229
+
145
230
  error_nodes = _find_node_type(filename, lines, node, "ERROR")
146
231
  if error_nodes:
147
232
  for node in error_nodes:
@@ -154,15 +239,42 @@ def _walk(filename, lines, node) -> int:
154
239
  line = node.range.start_point[0] + 1
155
240
  column = node.range.start_point[1] + 1
156
241
 
157
- errors = 0
158
- for node in _find_nodes(filename, lines, node):
159
- errors += _single_node_checks(filename, lines, node)
242
+ return _stateful_walk(filename, lines, node, user_definition, strict)
160
243
 
161
- return errors
244
+
245
+ def _parse_user_definition(filename, lines, root_node):
246
+ promise_blocks = _find_node_type(filename, lines, root_node, "promise_block_name")
247
+ bundle_blocks = _find_node_type(filename, lines, root_node, "bundle_block_name")
248
+ body_blocks = _find_node_type(filename, lines, root_node, "body_block_name")
249
+
250
+ return {
251
+ "custom_promise_types": {_text(x) for x in promise_blocks},
252
+ "all_bundle_names": {_text(x) for x in bundle_blocks},
253
+ "all_body_names": {_text(x) for x in body_blocks},
254
+ }
255
+
256
+
257
+ def _parse_policy_file(filename):
258
+ assert os.path.isfile(filename)
259
+ PY_LANGUAGE = Language(tscfengine.language())
260
+ parser = Parser(PY_LANGUAGE)
261
+
262
+ with open(filename, "rb") as f:
263
+ original_data = f.read()
264
+ tree = parser.parse(original_data)
265
+ lines = original_data.decode().split("\n")
266
+
267
+ return tree, lines, original_data
162
268
 
163
269
 
164
270
  def lint_policy_file(
165
- filename, original_filename=None, original_line=None, snippet=None, prefix=None
271
+ filename,
272
+ original_filename=None,
273
+ original_line=None,
274
+ snippet=None,
275
+ prefix=None,
276
+ user_definition=None,
277
+ strict=True,
166
278
  ):
167
279
  assert original_filename is None or type(original_filename) is str
168
280
  assert original_line is None or type(original_line) is int
@@ -177,14 +289,11 @@ def lint_policy_file(
177
289
  assert snippet and snippet > 0
178
290
  assert os.path.isfile(filename)
179
291
  assert filename.endswith((".cf", ".cfengine3", ".cf3", ".cf.sub"))
180
- PY_LANGUAGE = Language(tscfengine.language())
181
- parser = Parser(PY_LANGUAGE)
182
292
 
183
- with open(filename, "rb") as f:
184
- original_data = f.read()
185
- tree = parser.parse(original_data)
186
- lines = original_data.decode().split("\n")
293
+ if user_definition is None:
294
+ user_definition = {}
187
295
 
296
+ tree, lines, original_data = _parse_policy_file(filename)
188
297
  root_node = tree.root_node
189
298
  if root_node.type != "source_file":
190
299
  if snippet:
@@ -214,7 +323,7 @@ def lint_policy_file(
214
323
  else:
215
324
  print(f"Error: Empty policy file '{filename}'")
216
325
  errors += 1
217
- errors += _walk(filename, lines, root_node)
326
+ errors += _walk(filename, lines, root_node, user_definition, strict)
218
327
  if prefix:
219
328
  print(prefix, end="")
220
329
  if errors == 0:
@@ -235,8 +344,9 @@ def lint_policy_file(
235
344
  return errors
236
345
 
237
346
 
238
- def lint_folder(folder):
347
+ def lint_folder(folder, strict=True):
239
348
  errors = 0
349
+ policy_files = []
240
350
  while folder.endswith(("/.", "/")):
241
351
  folder = folder[0:-1]
242
352
  for filename in itertools.chain(
@@ -246,22 +356,44 @@ def lint_folder(folder):
246
356
  continue
247
357
  if filename.startswith(".") and not filename.startswith("./"):
248
358
  continue
249
- errors += lint_single_file(filename)
359
+
360
+ if filename.endswith((".cf", ".cfengine3", ".cf3", ".cf.sub")):
361
+ policy_files.append(filename)
362
+ else:
363
+ errors += lint_single_file(filename)
364
+
365
+ user_definition = {}
366
+
367
+ # First pass: Gather custom types
368
+ for filename in policy_files if strict else []:
369
+ tree, lines, _ = _parse_policy_file(filename)
370
+ if tree.root_node.type == "source_file":
371
+ for key, val in _parse_user_definition(
372
+ filename, lines, tree.root_node
373
+ ).items():
374
+ user_definition[key] = user_definition.get(key, set()).union(val)
375
+
376
+ # Second pass: lint all policy files
377
+ for filename in policy_files:
378
+ errors += lint_policy_file(
379
+ filename, user_definition=user_definition, strict=strict
380
+ )
250
381
  return errors
251
382
 
252
383
 
253
- def lint_single_file(file):
384
+ def lint_single_file(file, user_definition=None, strict=True):
254
385
  assert os.path.isfile(file)
255
386
  if file.endswith("/cfbs.json"):
256
387
  return lint_cfbs_json(file)
257
388
  if file.endswith(".json"):
258
389
  return lint_json(file)
259
390
  assert file.endswith(".cf")
260
- return lint_policy_file(file)
391
+ return lint_policy_file(file, user_definition=user_definition, strict=strict)
261
392
 
262
393
 
263
- def lint_single_arg(arg):
394
+ def lint_single_arg(arg, strict=True):
264
395
  if os.path.isdir(arg):
265
- return lint_folder(arg)
396
+ return lint_folder(arg, strict)
266
397
  assert os.path.isfile(arg)
267
- return lint_single_file(arg)
398
+
399
+ return lint_single_file(arg, strict)
@@ -52,6 +52,12 @@ def _get_arg_parser():
52
52
  "lint",
53
53
  help="Look for syntax errors and other simple mistakes",
54
54
  )
55
+ lnt.add_argument(
56
+ "--strict",
57
+ type=str,
58
+ default="yes",
59
+ help="Strict mode. Default=yes, checks for undefined promise types, bundles, bodies, functions",
60
+ )
55
61
  lnt.add_argument("files", nargs="*", help="Files to format")
56
62
  subp.add_parser(
57
63
  "report",
@@ -132,7 +138,7 @@ def run_command_with_args(args) -> int:
132
138
  if args.command == "format":
133
139
  return commands.format(args.files, args.line_length)
134
140
  if args.command == "lint":
135
- return commands.lint(args.files)
141
+ return commands.lint(args.files, (args.strict.lower() in ("y", "ye", "yes")))
136
142
  if args.command == "report":
137
143
  return commands.report()
138
144
  if args.command == "run":
@@ -0,0 +1,239 @@
1
+ # These constants are temporary and may change in the future
2
+ # TODO: Find a way to extract these from the generated "syntax-definition"-json file
3
+
4
+ DEPRECATED_PROMISE_TYPES = ["defaults", "guest_environments"]
5
+ ALLOWED_BUNDLE_TYPES = ["agent", "common", "monitor", "server", "edit_line", "edit_xml"]
6
+ BUILTIN_PROMISE_TYPES = {
7
+ "access",
8
+ "build_xpath",
9
+ "classes",
10
+ "commands",
11
+ "databases",
12
+ "defaults",
13
+ "delete_attribute",
14
+ "delete_lines",
15
+ "delete_text",
16
+ "delete_tree",
17
+ "field_edits",
18
+ "files",
19
+ "guest_environments",
20
+ "insert_lines",
21
+ "insert_text",
22
+ "insert_tree",
23
+ "measurements",
24
+ "meta",
25
+ "methods",
26
+ "packages",
27
+ "processes",
28
+ "replace_patterns",
29
+ "reports",
30
+ "roles",
31
+ "services",
32
+ "set_attribute",
33
+ "set_text",
34
+ "storage",
35
+ "users",
36
+ "vars",
37
+ }
38
+ BUILTIN_FUNCTIONS = {
39
+ "accessedbefore",
40
+ "accumulated",
41
+ "ago",
42
+ "and",
43
+ "basename",
44
+ "bundlesmatching",
45
+ "bundlestate",
46
+ "callstack_callers",
47
+ "callstack_promisers",
48
+ "canonify",
49
+ "canonifyuniquely",
50
+ "cf_version_after",
51
+ "cf_version_at",
52
+ "cf_version_before",
53
+ "cf_version_between",
54
+ "cf_version_maximum",
55
+ "cf_version_minimum",
56
+ "changedbefore",
57
+ "classesmatching",
58
+ "classfiltercsv",
59
+ "classfilterdata",
60
+ "classify",
61
+ "classmatch",
62
+ "concat",
63
+ "countclassesmatching",
64
+ "countlinesmatching",
65
+ "data_expand",
66
+ "data_readstringarray",
67
+ "data_readstringarrayidx",
68
+ "data_regextract",
69
+ "data_sysctlvalues",
70
+ "datastate",
71
+ "difference",
72
+ "dirname",
73
+ "diskfree",
74
+ "escape",
75
+ "eval",
76
+ "every",
77
+ "execresult",
78
+ "execresult_as_data",
79
+ "expandrange",
80
+ "file_hash",
81
+ "fileexists",
82
+ "filesexist",
83
+ "filesize",
84
+ "filestat",
85
+ "filter",
86
+ "findfiles",
87
+ "findfiles_up",
88
+ "findlocalgroups",
89
+ "findlocalusers",
90
+ "findprocesses",
91
+ "format",
92
+ "getacls",
93
+ "getbundlemetatags",
94
+ "getclassmetatags",
95
+ "getenv",
96
+ "getfields",
97
+ "getgid",
98
+ "getgroupinfo",
99
+ "getgroups",
100
+ "getindices",
101
+ "getuid",
102
+ "getuserinfo",
103
+ "getusers",
104
+ "getvalues",
105
+ "getvariablemetatags",
106
+ "grep",
107
+ "groupexists",
108
+ "hash",
109
+ "hash_to_int",
110
+ "hashmatch",
111
+ "host2ip",
112
+ "hostinnetgroup",
113
+ "hostrange",
114
+ "hostsseen",
115
+ "hostswithclass",
116
+ "hostswithgroup",
117
+ "hubknowledge",
118
+ "ifelse",
119
+ "int",
120
+ "intersection",
121
+ "ip2host",
122
+ "iprange",
123
+ "irange",
124
+ "is_type",
125
+ "isconnectable",
126
+ "isdir",
127
+ "isexecutable",
128
+ "isgreaterthan",
129
+ "isipinsubnet",
130
+ "islessthan",
131
+ "islink",
132
+ "isnewerthan",
133
+ "isnewerthantime",
134
+ "isplain",
135
+ "isreadable",
136
+ "isvariable",
137
+ "join",
138
+ "lastnode",
139
+ "laterthan",
140
+ "ldaparray",
141
+ "ldaplist",
142
+ "ldapvalue",
143
+ "length",
144
+ "lsdir",
145
+ "makerule",
146
+ "maparray",
147
+ "mapdata",
148
+ "maplist",
149
+ "max",
150
+ "mean",
151
+ "mergedata",
152
+ "min",
153
+ "network_connections",
154
+ "none",
155
+ "not",
156
+ "now",
157
+ "nth",
158
+ "on",
159
+ "or",
160
+ "packagesmatching",
161
+ "packageupdatesmatching",
162
+ "parseintarray",
163
+ "parsejson",
164
+ "parserealarray",
165
+ "parsestringarray",
166
+ "parsestringarrayidx",
167
+ "parseyaml",
168
+ "peerleader",
169
+ "peerleaders",
170
+ "peers",
171
+ "processexists",
172
+ "product",
173
+ "randomint",
174
+ "read_module_protocol",
175
+ "readcsv",
176
+ "readdata",
177
+ "readenvfile",
178
+ "readfile",
179
+ "readintarray",
180
+ "readintlist",
181
+ "readjson",
182
+ "readrealarray",
183
+ "readreallist",
184
+ "readstringarray",
185
+ "readstringarrayidx",
186
+ "readstringlist",
187
+ "readtcp",
188
+ "readyaml",
189
+ "regarray",
190
+ "regcmp",
191
+ "regex_replace",
192
+ "regextract",
193
+ "registryvalue",
194
+ "regldap",
195
+ "regline",
196
+ "reglist",
197
+ "remoteclassesmatching",
198
+ "remotescalar",
199
+ "returnszero",
200
+ "reverse",
201
+ "rrange",
202
+ "search_up",
203
+ "selectservers",
204
+ "shuffle",
205
+ "some",
206
+ "sort",
207
+ "splayclass",
208
+ "splitstring",
209
+ "storejson",
210
+ "strcmp",
211
+ "strftime",
212
+ "string",
213
+ "string_downcase",
214
+ "string_head",
215
+ "string_length",
216
+ "string_mustache",
217
+ "string_replace",
218
+ "string_reverse",
219
+ "string_split",
220
+ "string_tail",
221
+ "string_trim",
222
+ "string_upcase",
223
+ "sublist",
224
+ "sum",
225
+ "sysctlvalue",
226
+ "translatepath",
227
+ "type",
228
+ "unique",
229
+ "url_get",
230
+ "usemodule",
231
+ "userexists",
232
+ "useringroup",
233
+ "validdata",
234
+ "validjson",
235
+ "variablesmatching",
236
+ "variablesmatching_as_data",
237
+ "variance",
238
+ "version_compare",
239
+ }
@@ -329,7 +329,7 @@ wheels = [
329
329
 
330
330
  [[package]]
331
331
  name = "requests"
332
- version = "2.32.5"
332
+ version = "2.33.0"
333
333
  source = { registry = "https://pypi.org/simple" }
334
334
  dependencies = [
335
335
  { name = "certifi" },
@@ -337,9 +337,9 @@ dependencies = [
337
337
  { name = "idna" },
338
338
  { name = "urllib3" },
339
339
  ]
340
- sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
340
+ sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" }
341
341
  wheels = [
342
- { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
342
+ { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" },
343
343
  ]
344
344
 
345
345
  [[package]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes