pwshpy 1.0.0__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 (120) hide show
  1. pwshpy/__init__.py +144 -0
  2. pwshpy/__init__conf__.py +90 -0
  3. pwshpy/__main__.py +9 -0
  4. pwshpy/adapters/__init__.py +14 -0
  5. pwshpy/adapters/cli/__init__.py +60 -0
  6. pwshpy/adapters/cli/commands/__init__.py +75 -0
  7. pwshpy/adapters/cli/commands/_common.py +66 -0
  8. pwshpy/adapters/cli/commands/acl.py +51 -0
  9. pwshpy/adapters/cli/commands/cim.py +56 -0
  10. pwshpy/adapters/cli/commands/config.py +335 -0
  11. pwshpy/adapters/cli/commands/connections.py +54 -0
  12. pwshpy/adapters/cli/commands/credentials.py +67 -0
  13. pwshpy/adapters/cli/commands/disks.py +50 -0
  14. pwshpy/adapters/cli/commands/dns.py +48 -0
  15. pwshpy/adapters/cli/commands/dotnet.py +158 -0
  16. pwshpy/adapters/cli/commands/elevation.py +40 -0
  17. pwshpy/adapters/cli/commands/environment.py +50 -0
  18. pwshpy/adapters/cli/commands/event_log.py +52 -0
  19. pwshpy/adapters/cli/commands/fileio.py +49 -0
  20. pwshpy/adapters/cli/commands/filesystem.py +134 -0
  21. pwshpy/adapters/cli/commands/info.py +76 -0
  22. pwshpy/adapters/cli/commands/local_accounts.py +72 -0
  23. pwshpy/adapters/cli/commands/logging.py +33 -0
  24. pwshpy/adapters/cli/commands/mutating.py +364 -0
  25. pwshpy/adapters/cli/commands/netcheck.py +56 -0
  26. pwshpy/adapters/cli/commands/networking.py +64 -0
  27. pwshpy/adapters/cli/commands/proc.py +49 -0
  28. pwshpy/adapters/cli/commands/processes.py +54 -0
  29. pwshpy/adapters/cli/commands/registry.py +71 -0
  30. pwshpy/adapters/cli/commands/scheduled_tasks.py +51 -0
  31. pwshpy/adapters/cli/commands/services.py +51 -0
  32. pwshpy/adapters/cli/commands/system.py +102 -0
  33. pwshpy/adapters/cli/commands/uptime.py +46 -0
  34. pwshpy/adapters/cli/commands/web.py +100 -0
  35. pwshpy/adapters/cli/constants.py +28 -0
  36. pwshpy/adapters/cli/context.py +155 -0
  37. pwshpy/adapters/cli/exit_codes.py +52 -0
  38. pwshpy/adapters/cli/main.py +120 -0
  39. pwshpy/adapters/cli/output.py +199 -0
  40. pwshpy/adapters/cli/py.typed +0 -0
  41. pwshpy/adapters/cli/root.py +219 -0
  42. pwshpy/adapters/cli/typed_click.py +42 -0
  43. pwshpy/adapters/config/__init__.py +25 -0
  44. pwshpy/adapters/config/defaultconfig.d/40-layered-config.toml +71 -0
  45. pwshpy/adapters/config/defaultconfig.d/90-logging.toml +432 -0
  46. pwshpy/adapters/config/defaultconfig.toml +44 -0
  47. pwshpy/adapters/config/deploy.py +118 -0
  48. pwshpy/adapters/config/display.py +56 -0
  49. pwshpy/adapters/config/loader.py +188 -0
  50. pwshpy/adapters/config/overrides.py +189 -0
  51. pwshpy/adapters/config/permissions.py +185 -0
  52. pwshpy/adapters/config/py.typed +0 -0
  53. pwshpy/adapters/logging/__init__.py +13 -0
  54. pwshpy/adapters/logging/py.typed +0 -0
  55. pwshpy/adapters/logging/setup.py +132 -0
  56. pwshpy/adapters/memory/__init__.py +41 -0
  57. pwshpy/adapters/memory/config.py +62 -0
  58. pwshpy/adapters/memory/logging.py +15 -0
  59. pwshpy/adapters/native/__init__.py +115 -0
  60. pwshpy/adapters/native/acl.py +94 -0
  61. pwshpy/adapters/native/acl_control.py +104 -0
  62. pwshpy/adapters/native/cim.py +96 -0
  63. pwshpy/adapters/native/credentials.py +122 -0
  64. pwshpy/adapters/native/dns.py +89 -0
  65. pwshpy/adapters/native/elevation.py +186 -0
  66. pwshpy/adapters/native/environment.py +30 -0
  67. pwshpy/adapters/native/event_log.py +116 -0
  68. pwshpy/adapters/native/event_log_control.py +49 -0
  69. pwshpy/adapters/native/fileio.py +179 -0
  70. pwshpy/adapters/native/filesystem.py +151 -0
  71. pwshpy/adapters/native/inventory.py +85 -0
  72. pwshpy/adapters/native/journald.py +148 -0
  73. pwshpy/adapters/native/local_account_control.py +159 -0
  74. pwshpy/adapters/native/local_accounts.py +104 -0
  75. pwshpy/adapters/native/marshal.py +431 -0
  76. pwshpy/adapters/native/netcheck.py +48 -0
  77. pwshpy/adapters/native/networking.py +107 -0
  78. pwshpy/adapters/native/posix_account_control.py +160 -0
  79. pwshpy/adapters/native/posix_accounts.py +86 -0
  80. pwshpy/adapters/native/posix_acl.py +269 -0
  81. pwshpy/adapters/native/process_control.py +91 -0
  82. pwshpy/adapters/native/process_exec.py +90 -0
  83. pwshpy/adapters/native/psutil_disk.py +56 -0
  84. pwshpy/adapters/native/psutil_net.py +72 -0
  85. pwshpy/adapters/native/psutil_process.py +73 -0
  86. pwshpy/adapters/native/psutil_system.py +41 -0
  87. pwshpy/adapters/native/registry.py +113 -0
  88. pwshpy/adapters/native/registry_control.py +79 -0
  89. pwshpy/adapters/native/scheduled_task_control.py +145 -0
  90. pwshpy/adapters/native/scheduled_tasks.py +124 -0
  91. pwshpy/adapters/native/secret_service.py +179 -0
  92. pwshpy/adapters/native/service_control.py +184 -0
  93. pwshpy/adapters/native/services.py +106 -0
  94. pwshpy/adapters/native/systemd_dbus.py +113 -0
  95. pwshpy/adapters/native/systemd_services.py +176 -0
  96. pwshpy/adapters/native/systemd_timers.py +249 -0
  97. pwshpy/adapters/native/web.py +140 -0
  98. pwshpy/adapters/powershell/__init__.py +17 -0
  99. pwshpy/adapters/powershell/hosted.py +453 -0
  100. pwshpy/adapters/py.typed +0 -0
  101. pwshpy/application/__init__.py +26 -0
  102. pwshpy/application/ports.py +486 -0
  103. pwshpy/application/py.typed +0 -0
  104. pwshpy/composition/__init__.py +104 -0
  105. pwshpy/composition/facade.py +1276 -0
  106. pwshpy/composition/py.typed +0 -0
  107. pwshpy/domain/__init__.py +53 -0
  108. pwshpy/domain/behaviors.py +5 -0
  109. pwshpy/domain/enums.py +367 -0
  110. pwshpy/domain/errors.py +114 -0
  111. pwshpy/domain/pipeline.py +147 -0
  112. pwshpy/domain/py.typed +0 -0
  113. pwshpy/domain/records.py +692 -0
  114. pwshpy/entry.py +26 -0
  115. pwshpy/py.typed +0 -0
  116. pwshpy-1.0.0.dist-info/METADATA +317 -0
  117. pwshpy-1.0.0.dist-info/RECORD +120 -0
  118. pwshpy-1.0.0.dist-info/WHEEL +4 -0
  119. pwshpy-1.0.0.dist-info/entry_points.txt +2 -0
  120. pwshpy-1.0.0.dist-info/licenses/LICENSE +22 -0
pwshpy/__init__.py ADDED
@@ -0,0 +1,144 @@
1
+ """Public package surface for pwshpy.
2
+
3
+ A genuinely Pythonic PowerShell: typed records and a fluent, lazy pipeline over
4
+ native Windows bindings (native), with an in-process PowerShell 7.6 SDK fallback
5
+ (.NET) — never a ``pwsh.exe`` subprocess.
6
+
7
+ Typical use::
8
+
9
+ from pwshpy import ps
10
+ for proc in ps.get_process().where(lambda p: p.name == "python").take(5):
11
+ print(proc.pid, proc.name)
12
+
13
+ This module re-exports the stable public API, routing through the proper
14
+ architectural layers (domain records/pipeline/errors, the wired ``ps`` facade,
15
+ and package metadata).
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ # Metadata
21
+ from .__init__conf__ import print_info
22
+
23
+ # Composition exports (wired adapters + the ps facade)
24
+ from .composition import Ps, build_ps, get_config, ps
25
+
26
+ # Domain object model
27
+ from .domain.enums import (
28
+ AceType,
29
+ AddressFamily,
30
+ ConnectionState,
31
+ EventLevel,
32
+ ProcessStatus,
33
+ RegistryHive,
34
+ RegistryValueType,
35
+ ServiceKind,
36
+ ServiceStartType,
37
+ ServiceState,
38
+ TaskState,
39
+ TransportProtocol,
40
+ WellKnownSid,
41
+ )
42
+ from .domain.errors import (
43
+ ConfigurationError,
44
+ ElevationRequiredError,
45
+ FeatureUnavailableError,
46
+ NativeCallError,
47
+ PlatformUnsupportedError,
48
+ PowerShellError,
49
+ PwshPyError,
50
+ )
51
+ from .domain.pipeline import Pipeline
52
+ from .domain.records import (
53
+ AclEntry,
54
+ CimInstance,
55
+ CommandInfo,
56
+ CommandParameter,
57
+ ComputerInfo,
58
+ ConnectionTest,
59
+ Credential,
60
+ DiskUsage,
61
+ DnsRecord,
62
+ EnvVar,
63
+ EventLogEntry,
64
+ FileSystemItem,
65
+ Hotfix,
66
+ LocalGroup,
67
+ LocalUser,
68
+ NetAdapter,
69
+ NetConnection,
70
+ NetIpAddress,
71
+ ProcessInfo,
72
+ ProcessResult,
73
+ PSInvocationResult,
74
+ PSObjectRecord,
75
+ PSRecord,
76
+ RegistryKey,
77
+ RegistryValue,
78
+ ScheduledTaskInfo,
79
+ ServiceInfo,
80
+ SystemUptime,
81
+ WebResponse,
82
+ )
83
+
84
+ __all__ = [
85
+ # Facade
86
+ "ps",
87
+ "Ps",
88
+ "build_ps",
89
+ # Object model
90
+ "Pipeline",
91
+ "PSRecord",
92
+ "ProcessInfo",
93
+ "ProcessResult",
94
+ "NetAdapter",
95
+ "NetConnection",
96
+ "NetIpAddress",
97
+ "DiskUsage",
98
+ "SystemUptime",
99
+ "WebResponse",
100
+ "DnsRecord",
101
+ "ComputerInfo",
102
+ "ConnectionTest",
103
+ "Credential",
104
+ "FileSystemItem",
105
+ "Hotfix",
106
+ "EnvVar",
107
+ "RegistryKey",
108
+ "RegistryValue",
109
+ "ServiceInfo",
110
+ "EventLogEntry",
111
+ "CimInstance",
112
+ "CommandInfo",
113
+ "CommandParameter",
114
+ "ScheduledTaskInfo",
115
+ "LocalUser",
116
+ "LocalGroup",
117
+ "AclEntry",
118
+ "PSObjectRecord",
119
+ "PSInvocationResult",
120
+ "ProcessStatus",
121
+ "ConnectionState",
122
+ "TransportProtocol",
123
+ "AddressFamily",
124
+ "RegistryHive",
125
+ "RegistryValueType",
126
+ "ServiceState",
127
+ "ServiceStartType",
128
+ "ServiceKind",
129
+ "EventLevel",
130
+ "TaskState",
131
+ "WellKnownSid",
132
+ "AceType",
133
+ # Errors
134
+ "PwshPyError",
135
+ "NativeCallError",
136
+ "PowerShellError",
137
+ "FeatureUnavailableError",
138
+ "PlatformUnsupportedError",
139
+ "ElevationRequiredError",
140
+ "ConfigurationError",
141
+ # Config + metadata
142
+ "get_config",
143
+ "print_info",
144
+ ]
@@ -0,0 +1,90 @@
1
+ """Static package metadata surfaced to CLI commands and documentation.
2
+
3
+ Purpose
4
+ -------
5
+ Expose the current project metadata as simple constants. These values are kept
6
+ in sync with ``pyproject.toml`` by development automation (tests, push
7
+ pipelines), so runtime code does not query packaging metadata.
8
+
9
+ Contents
10
+ --------
11
+ * Module-level constants describing the published package.
12
+ * :func:`print_info` rendering the constants for the CLI ``info`` command.
13
+
14
+ System Role
15
+ -----------
16
+ Lives in the adapters/platform layer; CLI transports import these constants to
17
+ present authoritative project information without invoking packaging APIs.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import sys
23
+
24
+ __all__ = [
25
+ "name",
26
+ "title",
27
+ "version",
28
+ "homepage",
29
+ "author",
30
+ "author_email",
31
+ "shell_command",
32
+ "LAYEREDCONF_VENDOR",
33
+ "LAYEREDCONF_APP",
34
+ "LAYEREDCONF_SLUG",
35
+ "print_info",
36
+ ]
37
+
38
+ #: Distribution name declared in ``pyproject.toml``.
39
+ name = "pwshpy"
40
+ #: Human-readable summary shown in CLI help output.
41
+ title = "A Pythonic PowerShell: typed records + a fluent pipeline over native bindings, never a pwsh subprocess."
42
+ #: Current release version pulled from ``pyproject.toml`` by automation.
43
+ version = "1.0.0"
44
+ #: Repository homepage presented to users.
45
+ homepage = "https://github.com/bitranox/pwshpy"
46
+ #: Author attribution surfaced in CLI output.
47
+ author = "bitranox"
48
+ #: Contact email surfaced in CLI output.
49
+ author_email = "bitranox@gmail.com"
50
+ #: Console-script name published by the package.
51
+ shell_command = "pwshpy"
52
+
53
+ #: Vendor identifier for lib_layered_config paths (macOS/Windows)
54
+ LAYEREDCONF_VENDOR: str = "bitranox"
55
+ #: Application display name for lib_layered_config paths (macOS/Windows)
56
+ LAYEREDCONF_APP: str = "pwshpy"
57
+ #: Configuration slug for lib_layered_config Linux paths and environment variables
58
+ LAYEREDCONF_SLUG: str = "pwshpy"
59
+
60
+
61
+ def print_info() -> None:
62
+ """Print the summarised metadata block used by the CLI ``info`` command.
63
+
64
+ Why
65
+ Provides a single, auditable rendering function so documentation and
66
+ CLI output always match the system design reference.
67
+
68
+ Side Effects
69
+ Writes to ``stdout``.
70
+
71
+ Examples
72
+ --------
73
+ >>> print_info() # doctest: +ELLIPSIS
74
+ Info for pwshpy:
75
+ ...
76
+ """
77
+
78
+ fields = [
79
+ ("name", name),
80
+ ("title", title),
81
+ ("version", version),
82
+ ("homepage", homepage),
83
+ ("author", author),
84
+ ("author_email", author_email),
85
+ ("shell_command", shell_command),
86
+ ]
87
+ pad = max(len(label) for label, _ in fields)
88
+ lines = [f"Info for {name}:", ""]
89
+ lines.extend(f" {label.ljust(pad)} = {value}" for label, value in fields)
90
+ sys.stdout.write("\n".join(lines) + "\n")
pwshpy/__main__.py ADDED
@@ -0,0 +1,9 @@
1
+ """Module entry point for ``python -m pwshpy``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .adapters.cli.main import main
6
+ from .composition import build_production
7
+
8
+ if __name__ == "__main__":
9
+ raise SystemExit(main(services_factory=build_production))
@@ -0,0 +1,14 @@
1
+ """Adapters layer - infrastructure and framework integrations.
2
+
3
+ Contains adapter implementations that connect the application to external
4
+ systems and frameworks (CLI, configuration, logging).
5
+
6
+ Contents:
7
+ * :mod:`.config` - Configuration loading, deployment, and display
8
+ * :mod:`.logging` - Logging setup with lib_log_rich
9
+ * :mod:`.cli` - Click CLI framework integration
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ __all__: list[str] = []
@@ -0,0 +1,60 @@
1
+ """CLI package providing the command-line interface.
2
+
3
+ Re-exports all public symbols from submodules for convenient access.
4
+
5
+ Contents:
6
+ * Click context helpers and traceback state management from :mod:`.context`
7
+ * Root command group from :mod:`.root`
8
+ * Entry point from :mod:`.main`
9
+ * All command functions from :mod:`.commands`
10
+
11
+ System Role:
12
+ Acts as the public facade for the CLI subsystem. Consumers import from here
13
+ and remain insulated from internal module boundaries.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from .commands import (
19
+ cli_config,
20
+ cli_config_deploy,
21
+ cli_config_generate_examples,
22
+ cli_fail,
23
+ cli_info,
24
+ cli_logdemo,
25
+ )
26
+ from .constants import CLICK_CONTEXT_SETTINGS, TRACEBACK_SUMMARY_LIMIT, TRACEBACK_VERBOSE_LIMIT
27
+ from .context import (
28
+ TracebackState,
29
+ apply_traceback_preferences,
30
+ restore_traceback_state,
31
+ snapshot_traceback_state,
32
+ store_cli_context,
33
+ )
34
+ from .main import main
35
+ from .root import cli
36
+
37
+ __all__ = [
38
+ # Constants
39
+ "CLICK_CONTEXT_SETTINGS",
40
+ "TRACEBACK_SUMMARY_LIMIT",
41
+ "TRACEBACK_VERBOSE_LIMIT",
42
+ # Traceback management
43
+ "TracebackState",
44
+ "apply_traceback_preferences",
45
+ "restore_traceback_state",
46
+ "snapshot_traceback_state",
47
+ # Context helpers
48
+ "store_cli_context",
49
+ # Root command
50
+ "cli",
51
+ # Entry point
52
+ "main",
53
+ # Commands
54
+ "cli_config",
55
+ "cli_config_deploy",
56
+ "cli_config_generate_examples",
57
+ "cli_fail",
58
+ "cli_info",
59
+ "cli_logdemo",
60
+ ]
@@ -0,0 +1,75 @@
1
+ """CLI command implementations.
2
+
3
+ Collects all subcommand functions and re-exports them for registration
4
+ with the root CLI group.
5
+
6
+ Contents:
7
+ * Info commands from :mod:`.info`
8
+ * Config commands from :mod:`.config`
9
+ * Logging commands from :mod:`.logging`
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from .acl import cli_acl
15
+ from .cim import cli_cim
16
+ from .config import cli_config, cli_config_deploy, cli_config_generate_examples
17
+ from .connections import cli_connections
18
+ from .credentials import CREDENTIAL_COMMANDS
19
+ from .disks import cli_disks
20
+ from .dns import cli_resolve
21
+ from .dotnet import DOTNET_COMMANDS
22
+ from .elevation import cli_is_elevated
23
+ from .environment import cli_env
24
+ from .event_log import cli_event_log
25
+ from .fileio import FILEIO_COMMANDS
26
+ from .filesystem import FILESYSTEM_COMMANDS
27
+ from .info import cli_fail, cli_info
28
+ from .local_accounts import cli_local_groups, cli_local_users
29
+ from .logging import cli_logdemo
30
+ from .mutating import MUTATING_COMMANDS
31
+ from .netcheck import cli_test_connection
32
+ from .networking import NETWORKING_COMMANDS
33
+ from .proc import PROC_COMMANDS
34
+ from .processes import cli_processes
35
+ from .registry import cli_get_item_property, cli_registry_keys
36
+ from .scheduled_tasks import cli_scheduled_tasks
37
+ from .services import cli_services
38
+ from .system import SYSTEM_COMMANDS
39
+ from .uptime import cli_uptime
40
+ from .web import WEB_COMMANDS
41
+
42
+ __all__ = [
43
+ "FILESYSTEM_COMMANDS",
44
+ "MUTATING_COMMANDS",
45
+ "NETWORKING_COMMANDS",
46
+ "CREDENTIAL_COMMANDS",
47
+ "FILEIO_COMMANDS",
48
+ "PROC_COMMANDS",
49
+ "SYSTEM_COMMANDS",
50
+ "DOTNET_COMMANDS",
51
+ "WEB_COMMANDS",
52
+ "cli_acl",
53
+ "cli_cim",
54
+ "cli_config",
55
+ "cli_config_deploy",
56
+ "cli_config_generate_examples",
57
+ "cli_connections",
58
+ "cli_disks",
59
+ "cli_env",
60
+ "cli_event_log",
61
+ "cli_fail",
62
+ "cli_info",
63
+ "cli_is_elevated",
64
+ "cli_local_groups",
65
+ "cli_local_users",
66
+ "cli_logdemo",
67
+ "cli_processes",
68
+ "cli_get_item_property",
69
+ "cli_registry_keys",
70
+ "cli_resolve",
71
+ "cli_scheduled_tasks",
72
+ "cli_services",
73
+ "cli_test_connection",
74
+ "cli_uptime",
75
+ ]
@@ -0,0 +1,66 @@
1
+ """Shared helpers for record-listing CLI commands (processes, connections, ...).
2
+
3
+ Contents:
4
+ * :func:`resolve_format` — map the mutually-exclusive output flags to a format.
5
+ * :func:`parse_pairs` — parse repeatable ``KEY=VALUE`` / ``Name: value`` options.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Iterable
11
+
12
+ import rich_click as click
13
+
14
+ from ..output import RecordFormat
15
+
16
+
17
+ def parse_pairs(pairs: Iterable[str], separator: str, label: str, *, strip: bool = False) -> dict[str, str]:
18
+ """Parse repeatable ``KEY<sep>VALUE`` CLI options into a dict (last wins on a repeated key).
19
+
20
+ Used for ``cmdlet -p KEY=VALUE`` and ``invoke_web_request -H 'Name: value'``. A pair with
21
+ no separator or an empty key raises :class:`click.BadParameter` naming ``label``.
22
+
23
+ Example:
24
+ >>> parse_pairs(["Path=C:/", "Recurse=true"], "=", "KEY=VALUE")
25
+ {'Path': 'C:/', 'Recurse': 'true'}
26
+ >>> parse_pairs(["Accept: application/json"], ":", "'Name: value'", strip=True)
27
+ {'Accept': 'application/json'}
28
+ >>> parse_pairs(["oops"], "=", "KEY=VALUE")
29
+ Traceback (most recent call last):
30
+ click.exceptions.BadParameter: expected KEY=VALUE, got 'oops'
31
+ """
32
+ result: dict[str, str] = {}
33
+ for pair in pairs:
34
+ key, separator_found, value = pair.partition(separator)
35
+ clean_key = key.strip() if strip else key
36
+ if not separator_found or not clean_key:
37
+ raise click.BadParameter(f"expected {label}, got {pair!r}")
38
+ result[clean_key] = value.strip() if strip else value
39
+ return result
40
+
41
+
42
+ def resolve_format(as_json: bool, as_jsonl: bool) -> RecordFormat:
43
+ """Pick the output format from mutually-exclusive flags (default: human table).
44
+
45
+ Raises:
46
+ click.UsageError: If both ``--json`` and ``--jsonl`` are given.
47
+
48
+ Example:
49
+ >>> resolve_format(False, True) is RecordFormat.JSONL
50
+ True
51
+ >>> resolve_format(False, False) is RecordFormat.HUMAN
52
+ True
53
+ >>> resolve_format(True, True)
54
+ Traceback (most recent call last):
55
+ click.exceptions.UsageError: --json and --jsonl are mutually exclusive.
56
+ """
57
+ if as_json and as_jsonl:
58
+ raise click.UsageError("--json and --jsonl are mutually exclusive.")
59
+ if as_jsonl:
60
+ return RecordFormat.JSONL
61
+ if as_json:
62
+ return RecordFormat.JSON
63
+ return RecordFormat.HUMAN
64
+
65
+
66
+ __all__ = ["parse_pairs", "resolve_format"]
@@ -0,0 +1,51 @@
1
+ """``pwshpy acl`` - list a path's DACL entries through the pipeline.
2
+
3
+ A thin front end over the ``ps`` facade (native / win32security), mirroring
4
+ ``Get-Acl``. Emits one :class:`~pwshpy.domain.records.AclEntry` per
5
+ access-control entry, identified by trustee SID (locale-independent). Filtering
6
+ is done in Python via the pipeline.
7
+
8
+ Contents:
9
+ * :func:`cli_acl` - the ``acl`` subcommand.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+
16
+ import lib_log_rich.runtime
17
+ import rich_click as click
18
+
19
+ from ..constants import CLICK_CONTEXT_SETTINGS
20
+ from ..context import get_cli_context
21
+ from ..output import emit
22
+ from ..typed_click import argument, option
23
+ from ._common import resolve_format
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @click.command("get_acl", context_settings=CLICK_CONTEXT_SETTINGS)
29
+ @argument("path")
30
+ @option("--json", "as_json", is_flag=True, default=False, help="Emit records as a JSON array.")
31
+ @option("--jsonl", "as_jsonl", is_flag=True, default=False, help="Stream one JSON record per line.")
32
+ @click.pass_context
33
+ def cli_acl(ctx: click.Context, path: str, as_json: bool, as_jsonl: bool) -> None:
34
+ """List the ACL entries of PATH - win32 DACLs on Windows, POSIX ACLs on Linux (native; like Get-Acl).
35
+
36
+ One record per access-control entry, identified by trustee SID. Filter in
37
+ Python via the pipeline, e.g. keep the Deny entries.
38
+
39
+ Example:
40
+ >>> from click.testing import CliRunner
41
+ >>> CliRunner().invoke(cli_acl, ["--help"]).exit_code
42
+ 0
43
+ """
44
+ fmt = resolve_format(as_json, as_jsonl)
45
+ ps = get_cli_context(ctx).services.ps
46
+ with lib_log_rich.runtime.bind(job_id="cli-acl", extra={"command": "acl"}):
47
+ logger.info("Reading ACL")
48
+ emit(ps.get_acl(path), fmt)
49
+
50
+
51
+ __all__ = ["cli_acl"]
@@ -0,0 +1,56 @@
1
+ """``pwshpy cim`` - query CIM/WMI instances through the pipeline.
2
+
3
+ A thin front end over the ``ps`` facade (native / WBEM), mirroring
4
+ ``Get-CimInstance``. Streams :class:`~pwshpy.domain.records.CimInstance` records
5
+ lazily (forward-only), so querying an unbounded class and piping to ``head`` stays
6
+ cheap. CIM instances are open key/value bags - view them with ``--jsonl``.
7
+
8
+ Contents:
9
+ * :func:`cli_cim` - the ``cim`` subcommand.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+
16
+ import lib_log_rich.runtime
17
+ import rich_click as click
18
+
19
+ from ..constants import CLICK_CONTEXT_SETTINGS
20
+ from ..context import get_cli_context
21
+ from ..output import emit
22
+ from ..typed_click import argument, option
23
+ from ._common import resolve_format
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @click.command("get_cim_instance", context_settings=CLICK_CONTEXT_SETTINGS)
29
+ @argument("class_name")
30
+ @option("--where", "where", default=None, help='Read-only WQL filter fragment, e.g. "DriveType = 3".')
31
+ @option("--namespace", "namespace", default="root/cimv2", help="WMI namespace (default: root/cimv2).")
32
+ @option("--json", "as_json", is_flag=True, default=False, help="Emit records as a JSON array.")
33
+ @option("--jsonl", "as_jsonl", is_flag=True, default=False, help="Stream one JSON record per line.")
34
+ @click.pass_context
35
+ def cli_cim(
36
+ ctx: click.Context, class_name: str, where: str | None, namespace: str, as_json: bool, as_jsonl: bool
37
+ ) -> None:
38
+ """Query CIM/WMI class CLASS_NAME as typed records (native; like Get-CimInstance).
39
+
40
+ CLASS_NAME is a WMI class, e.g. ``Win32_OperatingSystem``, ``Win32_LogicalDisk``.
41
+ Streams lazily, so even an unbounded class (``CIM_DataFile``) stays cheap with
42
+ ``--jsonl | head``.
43
+
44
+ Example:
45
+ >>> from click.testing import CliRunner
46
+ >>> CliRunner().invoke(cli_cim, ["--help"]).exit_code
47
+ 0
48
+ """
49
+ fmt = resolve_format(as_json, as_jsonl)
50
+ ps = get_cli_context(ctx).services.ps
51
+ with lib_log_rich.runtime.bind(job_id="cli-cim", extra={"command": "cim"}):
52
+ logger.info("Querying CIM/WMI")
53
+ emit(ps.get_cim_instance(class_name, where=where, namespace=namespace), fmt)
54
+
55
+
56
+ __all__ = ["cli_cim"]