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.
- pwshpy/__init__.py +144 -0
- pwshpy/__init__conf__.py +90 -0
- pwshpy/__main__.py +9 -0
- pwshpy/adapters/__init__.py +14 -0
- pwshpy/adapters/cli/__init__.py +60 -0
- pwshpy/adapters/cli/commands/__init__.py +75 -0
- pwshpy/adapters/cli/commands/_common.py +66 -0
- pwshpy/adapters/cli/commands/acl.py +51 -0
- pwshpy/adapters/cli/commands/cim.py +56 -0
- pwshpy/adapters/cli/commands/config.py +335 -0
- pwshpy/adapters/cli/commands/connections.py +54 -0
- pwshpy/adapters/cli/commands/credentials.py +67 -0
- pwshpy/adapters/cli/commands/disks.py +50 -0
- pwshpy/adapters/cli/commands/dns.py +48 -0
- pwshpy/adapters/cli/commands/dotnet.py +158 -0
- pwshpy/adapters/cli/commands/elevation.py +40 -0
- pwshpy/adapters/cli/commands/environment.py +50 -0
- pwshpy/adapters/cli/commands/event_log.py +52 -0
- pwshpy/adapters/cli/commands/fileio.py +49 -0
- pwshpy/adapters/cli/commands/filesystem.py +134 -0
- pwshpy/adapters/cli/commands/info.py +76 -0
- pwshpy/adapters/cli/commands/local_accounts.py +72 -0
- pwshpy/adapters/cli/commands/logging.py +33 -0
- pwshpy/adapters/cli/commands/mutating.py +364 -0
- pwshpy/adapters/cli/commands/netcheck.py +56 -0
- pwshpy/adapters/cli/commands/networking.py +64 -0
- pwshpy/adapters/cli/commands/proc.py +49 -0
- pwshpy/adapters/cli/commands/processes.py +54 -0
- pwshpy/adapters/cli/commands/registry.py +71 -0
- pwshpy/adapters/cli/commands/scheduled_tasks.py +51 -0
- pwshpy/adapters/cli/commands/services.py +51 -0
- pwshpy/adapters/cli/commands/system.py +102 -0
- pwshpy/adapters/cli/commands/uptime.py +46 -0
- pwshpy/adapters/cli/commands/web.py +100 -0
- pwshpy/adapters/cli/constants.py +28 -0
- pwshpy/adapters/cli/context.py +155 -0
- pwshpy/adapters/cli/exit_codes.py +52 -0
- pwshpy/adapters/cli/main.py +120 -0
- pwshpy/adapters/cli/output.py +199 -0
- pwshpy/adapters/cli/py.typed +0 -0
- pwshpy/adapters/cli/root.py +219 -0
- pwshpy/adapters/cli/typed_click.py +42 -0
- pwshpy/adapters/config/__init__.py +25 -0
- pwshpy/adapters/config/defaultconfig.d/40-layered-config.toml +71 -0
- pwshpy/adapters/config/defaultconfig.d/90-logging.toml +432 -0
- pwshpy/adapters/config/defaultconfig.toml +44 -0
- pwshpy/adapters/config/deploy.py +118 -0
- pwshpy/adapters/config/display.py +56 -0
- pwshpy/adapters/config/loader.py +188 -0
- pwshpy/adapters/config/overrides.py +189 -0
- pwshpy/adapters/config/permissions.py +185 -0
- pwshpy/adapters/config/py.typed +0 -0
- pwshpy/adapters/logging/__init__.py +13 -0
- pwshpy/adapters/logging/py.typed +0 -0
- pwshpy/adapters/logging/setup.py +132 -0
- pwshpy/adapters/memory/__init__.py +41 -0
- pwshpy/adapters/memory/config.py +62 -0
- pwshpy/adapters/memory/logging.py +15 -0
- pwshpy/adapters/native/__init__.py +115 -0
- pwshpy/adapters/native/acl.py +94 -0
- pwshpy/adapters/native/acl_control.py +104 -0
- pwshpy/adapters/native/cim.py +96 -0
- pwshpy/adapters/native/credentials.py +122 -0
- pwshpy/adapters/native/dns.py +89 -0
- pwshpy/adapters/native/elevation.py +186 -0
- pwshpy/adapters/native/environment.py +30 -0
- pwshpy/adapters/native/event_log.py +116 -0
- pwshpy/adapters/native/event_log_control.py +49 -0
- pwshpy/adapters/native/fileio.py +179 -0
- pwshpy/adapters/native/filesystem.py +151 -0
- pwshpy/adapters/native/inventory.py +85 -0
- pwshpy/adapters/native/journald.py +148 -0
- pwshpy/adapters/native/local_account_control.py +159 -0
- pwshpy/adapters/native/local_accounts.py +104 -0
- pwshpy/adapters/native/marshal.py +431 -0
- pwshpy/adapters/native/netcheck.py +48 -0
- pwshpy/adapters/native/networking.py +107 -0
- pwshpy/adapters/native/posix_account_control.py +160 -0
- pwshpy/adapters/native/posix_accounts.py +86 -0
- pwshpy/adapters/native/posix_acl.py +269 -0
- pwshpy/adapters/native/process_control.py +91 -0
- pwshpy/adapters/native/process_exec.py +90 -0
- pwshpy/adapters/native/psutil_disk.py +56 -0
- pwshpy/adapters/native/psutil_net.py +72 -0
- pwshpy/adapters/native/psutil_process.py +73 -0
- pwshpy/adapters/native/psutil_system.py +41 -0
- pwshpy/adapters/native/registry.py +113 -0
- pwshpy/adapters/native/registry_control.py +79 -0
- pwshpy/adapters/native/scheduled_task_control.py +145 -0
- pwshpy/adapters/native/scheduled_tasks.py +124 -0
- pwshpy/adapters/native/secret_service.py +179 -0
- pwshpy/adapters/native/service_control.py +184 -0
- pwshpy/adapters/native/services.py +106 -0
- pwshpy/adapters/native/systemd_dbus.py +113 -0
- pwshpy/adapters/native/systemd_services.py +176 -0
- pwshpy/adapters/native/systemd_timers.py +249 -0
- pwshpy/adapters/native/web.py +140 -0
- pwshpy/adapters/powershell/__init__.py +17 -0
- pwshpy/adapters/powershell/hosted.py +453 -0
- pwshpy/adapters/py.typed +0 -0
- pwshpy/application/__init__.py +26 -0
- pwshpy/application/ports.py +486 -0
- pwshpy/application/py.typed +0 -0
- pwshpy/composition/__init__.py +104 -0
- pwshpy/composition/facade.py +1276 -0
- pwshpy/composition/py.typed +0 -0
- pwshpy/domain/__init__.py +53 -0
- pwshpy/domain/behaviors.py +5 -0
- pwshpy/domain/enums.py +367 -0
- pwshpy/domain/errors.py +114 -0
- pwshpy/domain/pipeline.py +147 -0
- pwshpy/domain/py.typed +0 -0
- pwshpy/domain/records.py +692 -0
- pwshpy/entry.py +26 -0
- pwshpy/py.typed +0 -0
- pwshpy-1.0.0.dist-info/METADATA +317 -0
- pwshpy-1.0.0.dist-info/RECORD +120 -0
- pwshpy-1.0.0.dist-info/WHEEL +4 -0
- pwshpy-1.0.0.dist-info/entry_points.txt +2 -0
- 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
|
+
]
|
pwshpy/__init__conf__.py
ADDED
|
@@ -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"]
|