ominfra 0.0.0.dev154__py3-none-any.whl → 0.0.0.dev155__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. ominfra/manage/bootstrap.py +4 -0
  2. ominfra/manage/bootstrap_.py +5 -0
  3. ominfra/manage/commands/inject.py +8 -11
  4. ominfra/manage/commands/{execution.py → local.py} +1 -5
  5. ominfra/manage/commands/ping.py +23 -0
  6. ominfra/manage/commands/types.py +8 -0
  7. ominfra/manage/deploy/apps.py +72 -0
  8. ominfra/manage/deploy/config.py +8 -0
  9. ominfra/manage/deploy/git.py +136 -0
  10. ominfra/manage/deploy/inject.py +21 -0
  11. ominfra/manage/deploy/paths.py +81 -28
  12. ominfra/manage/deploy/types.py +13 -0
  13. ominfra/manage/deploy/venvs.py +66 -0
  14. ominfra/manage/inject.py +20 -4
  15. ominfra/manage/main.py +15 -27
  16. ominfra/manage/remote/_main.py +1 -1
  17. ominfra/manage/remote/config.py +0 -2
  18. ominfra/manage/remote/connection.py +7 -24
  19. ominfra/manage/remote/execution.py +1 -1
  20. ominfra/manage/remote/inject.py +3 -14
  21. ominfra/manage/system/commands.py +22 -2
  22. ominfra/manage/system/config.py +3 -1
  23. ominfra/manage/system/inject.py +16 -6
  24. ominfra/manage/system/packages.py +33 -7
  25. ominfra/manage/system/platforms.py +72 -0
  26. ominfra/manage/targets/__init__.py +0 -0
  27. ominfra/manage/targets/connection.py +150 -0
  28. ominfra/manage/targets/inject.py +42 -0
  29. ominfra/manage/targets/targets.py +87 -0
  30. ominfra/scripts/journald2aws.py +24 -7
  31. ominfra/scripts/manage.py +1880 -438
  32. ominfra/scripts/supervisor.py +187 -25
  33. ominfra/supervisor/configs.py +163 -18
  34. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/METADATA +3 -3
  35. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/RECORD +40 -29
  36. ominfra/manage/system/types.py +0 -5
  37. /ominfra/manage/{commands → deploy}/interp.py +0 -0
  38. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/LICENSE +0 -0
  39. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/WHEEL +0 -0
  40. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/entry_points.txt +0 -0
  41. {ominfra-0.0.0.dev154.dist-info → ominfra-0.0.0.dev155.dist-info}/top_level.txt +0 -0
ominfra/manage/inject.py CHANGED
@@ -6,8 +6,10 @@ from omlish.lite.inject import InjectorBindings
6
6
  from omlish.lite.inject import inj
7
7
  from omlish.lite.marshal import ObjMarshalerManager
8
8
 
9
+ from .bootstrap import MainBootstrap
9
10
  from .commands.inject import bind_commands
10
11
  from .config import MainConfig
12
+ from .deploy.config import DeployConfig
11
13
  from .deploy.inject import bind_deploy
12
14
  from .marshal import ObjMarshalerInstaller
13
15
  from .marshal import ObjMarshalerInstallers
@@ -15,6 +17,7 @@ from .remote.config import RemoteConfig
15
17
  from .remote.inject import bind_remote
16
18
  from .system.config import SystemConfig
17
19
  from .system.inject import bind_system
20
+ from .targets.inject import bind_targets
18
21
 
19
22
 
20
23
  ##
@@ -22,9 +25,13 @@ from .system.inject import bind_system
22
25
 
23
26
  def bind_main(
24
27
  *,
25
- main_config: MainConfig,
26
- remote_config: RemoteConfig,
27
- system_config: SystemConfig,
28
+ main_config: MainConfig = MainConfig(),
29
+
30
+ deploy_config: DeployConfig = DeployConfig(),
31
+ remote_config: RemoteConfig = RemoteConfig(),
32
+ system_config: SystemConfig = SystemConfig(),
33
+
34
+ main_bootstrap: ta.Optional[MainBootstrap] = None,
28
35
  ) -> InjectorBindings:
29
36
  lst: ta.List[InjectorBindingOrBindings] = [
30
37
  inj.bind(main_config),
@@ -33,7 +40,9 @@ def bind_main(
33
40
  main_config=main_config,
34
41
  ),
35
42
 
36
- bind_deploy(),
43
+ bind_deploy(
44
+ deploy_config=deploy_config,
45
+ ),
37
46
 
38
47
  bind_remote(
39
48
  remote_config=remote_config,
@@ -42,10 +51,17 @@ def bind_main(
42
51
  bind_system(
43
52
  system_config=system_config,
44
53
  ),
54
+
55
+ bind_targets(),
45
56
  ]
46
57
 
47
58
  #
48
59
 
60
+ if main_bootstrap is not None:
61
+ lst.append(inj.bind(main_bootstrap))
62
+
63
+ #
64
+
49
65
  def build_obj_marshaler_manager(insts: ObjMarshalerInstallers) -> ObjMarshalerManager:
50
66
  msh = ObjMarshalerManager()
51
67
  inst: ObjMarshalerInstaller
ominfra/manage/main.py CHANGED
@@ -6,7 +6,6 @@ manage.py -s 'docker run -i python:3.12'
6
6
  manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
7
7
  """
8
8
  import asyncio
9
- import contextlib
10
9
  import json
11
10
  import sys
12
11
  import typing as ta
@@ -22,22 +21,17 @@ from omlish.lite.pycharm import PycharmRemoteDebug
22
21
  from .bootstrap import MainBootstrap
23
22
  from .bootstrap_ import main_bootstrap
24
23
  from .commands.base import Command
25
- from .commands.base import CommandExecutor
26
- from .commands.execution import LocalCommandExecutor
27
24
  from .config import MainConfig
25
+ from .deploy.config import DeployConfig
28
26
  from .remote.config import RemoteConfig
29
- from .remote.connection import RemoteExecutionConnector
30
- from .remote.spawning import RemoteSpawning
27
+ from .targets.connection import ManageTargetConnector
28
+ from .targets.targets import ManageTarget
31
29
 
32
30
 
33
31
  class MainCli(ArgparseCli):
34
32
  @argparse_command(
35
33
  argparse_arg('--_payload-file'),
36
34
 
37
- argparse_arg('-s', '--shell'),
38
- argparse_arg('-q', '--shell-quote', action='store_true'),
39
- argparse_arg('--python', default='python3'),
40
-
41
35
  argparse_arg('--pycharm-debug-port', type=int),
42
36
  argparse_arg('--pycharm-debug-host'),
43
37
  argparse_arg('--pycharm-debug-version'),
@@ -46,8 +40,9 @@ class MainCli(ArgparseCli):
46
40
 
47
41
  argparse_arg('--debug', action='store_true'),
48
42
 
49
- argparse_arg('--local', action='store_true'),
43
+ argparse_arg('--deploy-home'),
50
44
 
45
+ argparse_arg('target'),
51
46
  argparse_arg('command', nargs='+'),
52
47
  )
53
48
  async def run(self) -> None:
@@ -58,6 +53,10 @@ class MainCli(ArgparseCli):
58
53
  debug=bool(self.args.debug),
59
54
  ),
60
55
 
56
+ deploy_config=DeployConfig(
57
+ deploy_home=self.args.deploy_home,
58
+ ),
59
+
61
60
  remote_config=RemoteConfig(
62
61
  payload_file=self.args._payload_file, # noqa
63
62
 
@@ -68,8 +67,6 @@ class MainCli(ArgparseCli):
68
67
  ) if self.args.pycharm_debug_port is not None else None,
69
68
 
70
69
  timebomb_delay_s=self.args.remote_timebomb_delay_s,
71
-
72
- use_in_process_remote_executor=True,
73
70
  ),
74
71
  )
75
72
 
@@ -83,6 +80,11 @@ class MainCli(ArgparseCli):
83
80
 
84
81
  msh = injector[ObjMarshalerManager]
85
82
 
83
+ ts = self.args.target
84
+ if not ts.startswith('{'):
85
+ ts = json.dumps({ts: {}})
86
+ tgt: ManageTarget = msh.unmarshal_obj(json.loads(ts), ManageTarget)
87
+
86
88
  cmds: ta.List[Command] = []
87
89
  cmd: Command
88
90
  for c in self.args.command:
@@ -93,21 +95,7 @@ class MainCli(ArgparseCli):
93
95
 
94
96
  #
95
97
 
96
- async with contextlib.AsyncExitStack() as es:
97
- ce: CommandExecutor
98
-
99
- if self.args.local:
100
- ce = injector[LocalCommandExecutor]
101
-
102
- else:
103
- tgt = RemoteSpawning.Target(
104
- shell=self.args.shell,
105
- shell_quote=self.args.shell_quote,
106
- python=self.args.python,
107
- )
108
-
109
- ce = await es.enter_async_context(injector[RemoteExecutionConnector].connect(tgt, bs)) # noqa
110
-
98
+ async with injector[ManageTargetConnector].connect(tgt) as ce:
111
99
  async def run_command(cmd: Command) -> None:
112
100
  res = await ce.try_execute(
113
101
  cmd,
@@ -20,7 +20,7 @@ from omlish.os.deathsig import set_process_deathsig
20
20
 
21
21
  from ...pyremote import pyremote_bootstrap_finalize
22
22
  from ..bootstrap import MainBootstrap
23
- from ..commands.execution import LocalCommandExecutor
23
+ from ..commands.local import LocalCommandExecutor
24
24
  from .channel import RemoteChannel
25
25
  from .channel import RemoteChannelImpl
26
26
  from .execution import _RemoteCommandHandler
@@ -20,5 +20,3 @@ class RemoteConfig:
20
20
  timebomb_delay_s: ta.Optional[float] = 60 * 60.
21
21
 
22
22
  heartbeat_interval_s: float = 3.
23
-
24
- use_in_process_remote_executor: bool = False
@@ -1,18 +1,18 @@
1
1
  # ruff: noqa: UP006 UP007
2
- import abc
3
2
  import asyncio
4
3
  import contextlib
5
4
  import typing as ta
6
5
 
7
6
  from omlish.asyncs.asyncio.channels import asyncio_create_bytes_channel
8
7
  from omlish.lite.cached import cached_nullary
8
+ from omlish.lite.contextmanagers import aclosing
9
9
  from omlish.lite.marshal import ObjMarshalerManager
10
10
 
11
11
  from ...pyremote import PyremoteBootstrapDriver
12
12
  from ...pyremote import PyremoteBootstrapOptions
13
13
  from ...pyremote import pyremote_build_bootstrap_cmd
14
14
  from ..bootstrap import MainBootstrap
15
- from ..commands.execution import LocalCommandExecutor
15
+ from ..commands.local import LocalCommandExecutor
16
16
  from ._main import _remote_execution_main # noqa
17
17
  from .channel import RemoteChannelImpl
18
18
  from .execution import RemoteCommandExecutor
@@ -25,20 +25,7 @@ from .spawning import RemoteSpawning
25
25
  ##
26
26
 
27
27
 
28
- class RemoteExecutionConnector(abc.ABC):
29
- @abc.abstractmethod
30
- def connect(
31
- self,
32
- tgt: RemoteSpawning.Target,
33
- bs: MainBootstrap,
34
- ) -> ta.AsyncContextManager[RemoteCommandExecutor]:
35
- raise NotImplementedError
36
-
37
-
38
- ##
39
-
40
-
41
- class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
28
+ class PyremoteRemoteExecutionConnector:
42
29
  def __init__(
43
30
  self,
44
31
  *,
@@ -104,7 +91,7 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
104
91
  await chan.send_obj(bs)
105
92
 
106
93
  rce: RemoteCommandExecutor
107
- async with contextlib.aclosing(RemoteCommandExecutor(chan)) as rce:
94
+ async with aclosing(RemoteCommandExecutor(chan)) as rce:
108
95
  await rce.start()
109
96
 
110
97
  yield rce
@@ -113,7 +100,7 @@ class PyremoteRemoteExecutionConnector(RemoteExecutionConnector):
113
100
  ##
114
101
 
115
102
 
116
- class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
103
+ class InProcessRemoteExecutionConnector:
117
104
  def __init__(
118
105
  self,
119
106
  *,
@@ -126,11 +113,7 @@ class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
126
113
  self._local_executor = local_executor
127
114
 
128
115
  @contextlib.asynccontextmanager
129
- async def connect(
130
- self,
131
- tgt: RemoteSpawning.Target,
132
- bs: MainBootstrap,
133
- ) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
116
+ async def connect(self) -> ta.AsyncGenerator[RemoteCommandExecutor, None]:
134
117
  r0, w0 = asyncio_create_bytes_channel()
135
118
  r1, w1 = asyncio_create_bytes_channel()
136
119
 
@@ -144,7 +127,7 @@ class InProcessRemoteExecutionConnector(RemoteExecutionConnector):
144
127
  rch_task = asyncio.create_task(rch.run()) # noqa
145
128
  try:
146
129
  rce: RemoteCommandExecutor
147
- async with contextlib.aclosing(RemoteCommandExecutor(local_chan)) as rce:
130
+ async with aclosing(RemoteCommandExecutor(local_chan)) as rce:
148
131
  await rce.start()
149
132
 
150
133
  yield rce
@@ -394,7 +394,7 @@ class RemoteCommandExecutor(CommandExecutor):
394
394
  self,
395
395
  cmd: Command,
396
396
  *,
397
- log: ta.Optional[logging.Logger] = None,
397
+ log: ta.Optional[logging.Logger] = None, # noqa
398
398
  omit_exc_object: bool = False,
399
399
  ) -> CommandOutputOrException:
400
400
  try:
@@ -8,7 +8,6 @@ from omlish.lite.inject import inj
8
8
  from .config import RemoteConfig
9
9
  from .connection import InProcessRemoteExecutionConnector
10
10
  from .connection import PyremoteRemoteExecutionConnector
11
- from .connection import RemoteExecutionConnector
12
11
  from .payload import RemoteExecutionPayloadFile
13
12
  from .spawning import RemoteSpawning
14
13
  from .spawning import SubprocessRemoteSpawning
@@ -23,20 +22,10 @@ def bind_remote(
23
22
 
24
23
  inj.bind(SubprocessRemoteSpawning, singleton=True),
25
24
  inj.bind(RemoteSpawning, to_key=SubprocessRemoteSpawning),
26
- ]
27
-
28
- #
29
25
 
30
- if remote_config.use_in_process_remote_executor:
31
- lst.extend([
32
- inj.bind(InProcessRemoteExecutionConnector, singleton=True),
33
- inj.bind(RemoteExecutionConnector, to_key=InProcessRemoteExecutionConnector),
34
- ])
35
- else:
36
- lst.extend([
37
- inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
38
- inj.bind(RemoteExecutionConnector, to_key=PyremoteRemoteExecutionConnector),
39
- ])
26
+ inj.bind(PyremoteRemoteExecutionConnector, singleton=True),
27
+ inj.bind(InProcessRemoteExecutionConnector, singleton=True),
28
+ ]
40
29
 
41
30
  #
42
31
 
@@ -1,10 +1,14 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import dataclasses as dc
3
+ import typing as ta
3
4
 
5
+ from omlish.lite.check import check
4
6
  from omlish.lite.logs import log
5
7
 
6
8
  from ..commands.base import Command
7
9
  from ..commands.base import CommandExecutor
10
+ from .packages import SystemPackage
11
+ from .packages import SystemPackageManager
8
12
 
9
13
 
10
14
  ##
@@ -12,13 +16,29 @@ from ..commands.base import CommandExecutor
12
16
 
13
17
  @dc.dataclass(frozen=True)
14
18
  class CheckSystemPackageCommand(Command['CheckSystemPackageCommand.Output']):
19
+ pkgs: ta.Sequence[str] = ()
20
+
21
+ def __post_init__(self) -> None:
22
+ check.not_isinstance(self.pkgs, str)
23
+
15
24
  @dc.dataclass(frozen=True)
16
25
  class Output(Command.Output):
17
- pass
26
+ pkgs: ta.Sequence[SystemPackage]
18
27
 
19
28
 
20
29
  class CheckSystemPackageCommandExecutor(CommandExecutor[CheckSystemPackageCommand, CheckSystemPackageCommand.Output]):
30
+ def __init__(
31
+ self,
32
+ *,
33
+ mgr: SystemPackageManager,
34
+ ) -> None:
35
+ super().__init__()
36
+
37
+ self._mgr = mgr
38
+
21
39
  async def execute(self, cmd: CheckSystemPackageCommand) -> CheckSystemPackageCommand.Output:
22
40
  log.info('Checking system package!')
23
41
 
24
- return CheckSystemPackageCommand.Output()
42
+ ret = await self._mgr.query(*cmd.pkgs)
43
+
44
+ return CheckSystemPackageCommand.Output(list(ret.values()))
@@ -2,7 +2,9 @@
2
2
  import dataclasses as dc
3
3
  import typing as ta
4
4
 
5
+ from .platforms import Platform
6
+
5
7
 
6
8
  @dc.dataclass(frozen=True)
7
9
  class SystemConfig:
8
- platform: ta.Optional[str] = None
10
+ platform: ta.Optional[Platform] = None
@@ -1,5 +1,4 @@
1
1
  # ruff: noqa: UP006 UP007
2
- import sys
3
2
  import typing as ta
4
3
 
5
4
  from omlish.lite.inject import InjectorBindingOrBindings
@@ -13,7 +12,12 @@ from .config import SystemConfig
13
12
  from .packages import AptSystemPackageManager
14
13
  from .packages import BrewSystemPackageManager
15
14
  from .packages import SystemPackageManager
16
- from .types import SystemPlatform
15
+ from .packages import YumSystemPackageManager
16
+ from .platforms import AmazonLinuxPlatform
17
+ from .platforms import DarwinPlatform
18
+ from .platforms import LinuxPlatform
19
+ from .platforms import Platform
20
+ from .platforms import detect_system_platform
17
21
 
18
22
 
19
23
  def bind_system(
@@ -26,18 +30,24 @@ def bind_system(
26
30
 
27
31
  #
28
32
 
29
- platform = system_config.platform or sys.platform
30
- lst.append(inj.bind(platform, key=SystemPlatform))
33
+ platform = system_config.platform or detect_system_platform()
34
+ lst.append(inj.bind(platform, key=Platform))
31
35
 
32
36
  #
33
37
 
34
- if platform == 'linux':
38
+ if isinstance(platform, AmazonLinuxPlatform):
39
+ lst.extend([
40
+ inj.bind(YumSystemPackageManager, singleton=True),
41
+ inj.bind(SystemPackageManager, to_key=YumSystemPackageManager),
42
+ ])
43
+
44
+ elif isinstance(platform, LinuxPlatform):
35
45
  lst.extend([
36
46
  inj.bind(AptSystemPackageManager, singleton=True),
37
47
  inj.bind(SystemPackageManager, to_key=AptSystemPackageManager),
38
48
  ])
39
49
 
40
- elif platform == 'darwin':
50
+ elif isinstance(platform, DarwinPlatform):
41
51
  lst.extend([
42
52
  inj.bind(BrewSystemPackageManager, singleton=True),
43
53
  inj.bind(SystemPackageManager, to_key=BrewSystemPackageManager),
@@ -79,28 +79,54 @@ class AptSystemPackageManager(SystemPackageManager):
79
79
  }
80
80
 
81
81
  async def update(self) -> None:
82
- await asyncio_subprocess_check_call('apt', 'update', env={**os.environ, **self._APT_ENV})
82
+ await asyncio_subprocess_check_call('sudo', 'apt', 'update', env={**os.environ, **self._APT_ENV})
83
83
 
84
84
  async def upgrade(self) -> None:
85
- await asyncio_subprocess_check_call('apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
85
+ await asyncio_subprocess_check_call('sudo', 'apt', 'upgrade', '-y', env={**os.environ, **self._APT_ENV})
86
86
 
87
87
  async def install(self, *packages: SystemPackageOrStr) -> None:
88
88
  pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
89
- await asyncio_subprocess_check_call('apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
89
+ await asyncio_subprocess_check_call('sudo', 'apt', 'install', '-y', *pns, env={**os.environ, **self._APT_ENV})
90
90
 
91
91
  async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
92
92
  pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
93
- cmd = ['dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns]
94
- stdout, stderr = await asyncio_subprocess_run(
95
- *cmd,
93
+ out = await asyncio_subprocess_run(
94
+ 'dpkg-query', '-W', '-f=${Package}=${Version}\n', *pns,
96
95
  capture_output=True,
97
96
  check=False,
98
97
  )
99
98
  d: ta.Dict[str, SystemPackage] = {}
100
- for l in check.not_none(stdout).decode('utf-8').strip().splitlines():
99
+ for l in check.not_none(out.stdout).decode('utf-8').strip().splitlines():
101
100
  n, v = l.split('=', 1)
102
101
  d[n] = SystemPackage(
103
102
  name=n,
104
103
  version=v,
105
104
  )
106
105
  return d
106
+
107
+
108
+ class YumSystemPackageManager(SystemPackageManager):
109
+ async def update(self) -> None:
110
+ await asyncio_subprocess_check_call('sudo', 'yum', 'check-update')
111
+
112
+ async def upgrade(self) -> None:
113
+ await asyncio_subprocess_check_call('sudo', 'yum', 'update')
114
+
115
+ async def install(self, *packages: SystemPackageOrStr) -> None:
116
+ pns = [p.name if isinstance(p, SystemPackage) else p for p in packages] # FIXME: versions
117
+ await asyncio_subprocess_check_call('sudo', 'yum', 'install', *pns)
118
+
119
+ async def query(self, *packages: SystemPackageOrStr) -> ta.Mapping[str, SystemPackage]:
120
+ pns = [p.name if isinstance(p, SystemPackage) else p for p in packages]
121
+ d: ta.Dict[str, SystemPackage] = {}
122
+ for pn in pns:
123
+ out = await asyncio_subprocess_run(
124
+ 'rpm', '-q', pn,
125
+ capture_output=True,
126
+ )
127
+ if not out.proc.returncode:
128
+ d[pn] = SystemPackage(
129
+ pn,
130
+ check.not_none(out.stdout).decode().strip(),
131
+ )
132
+ return d
@@ -0,0 +1,72 @@
1
+ import abc
2
+ import dataclasses as dc
3
+ import sys
4
+
5
+ from omlish.lite.cached import cached_nullary
6
+ from omlish.lite.logs import log
7
+ from omlish.os.linux import LinuxOsRelease
8
+
9
+
10
+ ##
11
+
12
+
13
+ @dc.dataclass(frozen=True)
14
+ class Platform(abc.ABC): # noqa
15
+ pass
16
+
17
+
18
+ class LinuxPlatform(Platform, abc.ABC):
19
+ pass
20
+
21
+
22
+ class UbuntuPlatform(LinuxPlatform):
23
+ pass
24
+
25
+
26
+ class AmazonLinuxPlatform(LinuxPlatform):
27
+ pass
28
+
29
+
30
+ class GenericLinuxPlatform(LinuxPlatform):
31
+ pass
32
+
33
+
34
+ class DarwinPlatform(Platform):
35
+ pass
36
+
37
+
38
+ class UnknownPlatform(Platform):
39
+ pass
40
+
41
+
42
+ ##
43
+
44
+
45
+ def _detect_system_platform() -> Platform:
46
+ plat = sys.platform
47
+
48
+ if plat == 'linux':
49
+ if (osr := LinuxOsRelease.read()) is None:
50
+ return GenericLinuxPlatform()
51
+
52
+ if osr.id == 'amzn':
53
+ return AmazonLinuxPlatform()
54
+
55
+ elif osr.id == 'ubuntu':
56
+ return UbuntuPlatform()
57
+
58
+ else:
59
+ return GenericLinuxPlatform()
60
+
61
+ elif plat == 'darwin':
62
+ return DarwinPlatform()
63
+
64
+ else:
65
+ return UnknownPlatform()
66
+
67
+
68
+ @cached_nullary
69
+ def detect_system_platform() -> Platform:
70
+ platform = _detect_system_platform()
71
+ log.info('Detected platform: %r', platform)
72
+ return platform
File without changes