cgse-core 2025.0.7__py3-none-any.whl → 2025.0.8.dev1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cgse-core
3
- Version: 2025.0.7
3
+ Version: 2025.0.8.dev1
4
4
  Summary: Core services for the CGSE framework
5
5
  Author: IVS KU Leuven
6
6
  Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
@@ -14,6 +14,7 @@ Requires-Dist: gitpython>=3.1.44
14
14
  Requires-Dist: prometheus-client>=0.21.1
15
15
  Requires-Dist: pyzmq==23.2.1
16
16
  Requires-Dist: rich>=13.9.4
17
+ Requires-Dist: typer>=0.15.1
17
18
  Description-Content-Type: text/markdown
18
19
 
19
20
  # The core services for the CGSE platform
@@ -3,7 +3,7 @@ cgse_core/settings.yaml,sha256=XCCcRzO_U08O1lKe6hgWmn2o4iqdF1XvshHmlf9LHjw,2719
3
3
  egse/confman/__init__.py,sha256=Woeffl3sRL-r3tfEgbtCSwZBd2GnctSt0zxULeE10c8,37831
4
4
  egse/confman/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  egse/confman/confman.yaml,sha256=V3TnUHEVlrHt2VZ1HFOGgRVjq_fhWAIAg5eslKddMEs,2909
6
- egse/confman/confman_cs.py,sha256=Yyhj0tvG0zQEEcsWZgPCsAW3-MDKL-9nAqXkFhVkoR4,6404
6
+ egse/confman/confman_cs.py,sha256=9vIOCphbnPf8i66BvWJJzMJynwggz0EQvZsrZ5_ZyPY,6412
7
7
  egse/logger/__init__.py,sha256=lkwftajGyfsUQe-iwi0IDU9w5rsIirdZPqAataKC3rU,8163
8
8
  egse/logger/__main__.py,sha256=RFCl0vmeNmHeXNDbziUP5sDwC014TqwJfc8xtDvZ_as,370
9
9
  egse/logger/log_cs.py,sha256=lFZe5z5U_k28L3qp3P1rQt1Ux0OxM3pLdNXIvKSiF9s,9303
@@ -14,9 +14,13 @@ egse/storage/__init__.py,sha256=m-ooUTD6_WBssSDMAYPIBgWph2YEpf6abvxghnbd49c,4001
14
14
  egse/storage/__main__.py,sha256=LI9fxlsFWmEd5LcWUB0xA8i7Yt6UHgnblB4G0aTi3pI,28
15
15
  egse/storage/persistence.py,sha256=e4kMTszUXQxqPWBfnA_3elRHaKQmXJvKri7LwXQZXdk,18246
16
16
  egse/storage/storage.yaml,sha256=zTRtRFbuMLBILsnlIphG6iWjI1Nav6tW7uOm4cUvFuk,2593
17
- egse/storage/storage_cs.py,sha256=Z-HnpeqF5QgRLjhEnW4jIQ6QKFdY55jK1XBUttxww_k,5447
18
- scripts/cgse.py,sha256=euz9Lw3Lh0X-DZct7jywTX6JQJPiBua_zIRhsERI-64,5182
19
- cgse_core-2025.0.7.dist-info/METADATA,sha256=Hpw7WwUCtrnG6OgRi8Zh7arYjjBqbgEWeg5-mw399lY,631
20
- cgse_core-2025.0.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
- cgse_core-2025.0.7.dist-info/entry_points.txt,sha256=ZsNj5Fzv7_a7M-kGGZCUd8g6EK8_lonvbKK-OC8MlWk,268
22
- cgse_core-2025.0.7.dist-info/RECORD,,
17
+ egse/storage/storage_cs.py,sha256=6GHCFYDf7hI1N0FSp7ZoLtnKd9614YjvWMkbQVIPXb4,5314
18
+ scripts/_start.py,sha256=kRL_qE-PbZp5UGGgRpHsj5p_i3XEYXmgTVREvVNPtNQ,1034
19
+ scripts/_status.py,sha256=2zRse9p-aMafaa4ZfSHWU7QxynyI988hQfvpo3Z8WD8,2004
20
+ scripts/_stop.py,sha256=G9RlgJ3OHh8QPwuScc76hK-31koID0s8pg5Xg09Wzxg,1034
21
+ scripts/cgse.py,sha256=humjUqGl1Lc-XMcjGOcioHA3x8Ill8e2iKAEx8SxBtk,3495
22
+ scripts/services.py,sha256=a5VXxCyKFisoAvWOHkijQR_9jJDej_ougTfFa7CVUos,1112
23
+ cgse_core-2025.0.8.dev1.dist-info/METADATA,sha256=zEpv_uizw1hBQJO0SUfPOdk6AJFV4nfknjUWKHCKNIw,665
24
+ cgse_core-2025.0.8.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
25
+ cgse_core-2025.0.8.dev1.dist-info/entry_points.txt,sha256=NBNTUjW767NSHFvRU55mjtrmNaD7cHs9cB3FEqIeGXg,268
26
+ cgse_core-2025.0.8.dev1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  [console_scripts]
2
- cgse = scripts.cgse:cli
2
+ cgse = scripts.cgse:app
3
3
  cm_cs = egse.confman.confman_cs:cli
4
4
  log_cs = egse.logger.log_cs:cli
5
5
  pm_cs = egse.procman.procman_cs:cli
@@ -153,7 +153,7 @@ def status():
153
153
  import rich
154
154
  from egse.confman import get_status
155
155
 
156
- rich.print(get_status())
156
+ rich.print(get_status(), end='')
157
157
 
158
158
 
159
159
  @cli.command()
@@ -17,7 +17,6 @@ from apscheduler.schedulers.background import BackgroundScheduler
17
17
  from prometheus_client import start_http_server
18
18
  from pytz import utc
19
19
 
20
- from egse.bits import humanize_bytes
21
20
  from egse.control import ControlServer
22
21
  from egse.env import get_data_storage_location
23
22
  from egse.env import get_site_id
@@ -26,8 +25,6 @@ from egse.settings import Settings
26
25
  from egse.storage import StorageProtocol
27
26
  from egse.storage import StorageProxy
28
27
  from egse.storage import cycle_daily_files
29
- from egse.storage import is_storage_manager_active
30
- from egse.system import replace_environment_variable
31
28
 
32
29
  # Use explicit name here otherwise the logger will probably be called __main__
33
30
 
@@ -145,7 +142,7 @@ def status(full):
145
142
  import rich
146
143
  from egse.storage import get_status
147
144
 
148
- rich.print(get_status(full=full))
145
+ rich.print(get_status(full=full), end='')
149
146
 
150
147
 
151
148
  def check_prerequisites():
scripts/_start.py ADDED
@@ -0,0 +1,41 @@
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import rich
6
+
7
+
8
+ def start_log_cs():
9
+ rich.print("Starting the logging core service...")
10
+
11
+ out = open(Path('~/.log_cs.start.out').expanduser(), 'w')
12
+
13
+ subprocess.Popen(
14
+ [sys.executable, '-m', 'egse.logger.log_cs', 'start'],
15
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
16
+ close_fds=True
17
+ )
18
+
19
+
20
+ def start_sm_cs():
21
+ rich.print("Starting the storage manager core service...")
22
+
23
+ out = open(Path('~/.sm_cs.start.out').expanduser(), 'w')
24
+
25
+ subprocess.Popen(
26
+ [sys.executable, '-m', 'egse.storage.storage_cs', 'start'],
27
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
28
+ close_fds=True
29
+ )
30
+
31
+
32
+ def start_cm_cs():
33
+ rich.print("Starting the configuration manager core service...")
34
+
35
+ out = open(Path('~/.cm_cs.start.out').expanduser(), 'w')
36
+
37
+ subprocess.Popen(
38
+ [sys.executable, '-m', 'egse.confman.confman_cs', 'start'],
39
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
40
+ close_fds=True
41
+ )
scripts/_status.py ADDED
@@ -0,0 +1,81 @@
1
+ import asyncio
2
+ # import subprocess
3
+ import sys
4
+
5
+ import rich
6
+
7
+
8
+ async def run_all_status():
9
+ tasks = [
10
+ status_log_cs(),
11
+ status_sm_cs(),
12
+ status_cm_cs(),
13
+ ]
14
+
15
+ await asyncio.gather(*tasks)
16
+
17
+
18
+ async def status_log_cs():
19
+
20
+ proc = await asyncio.create_subprocess_exec(
21
+ sys.executable, '-m', 'egse.logger.log_cs', 'status',
22
+ stdout=asyncio.subprocess.PIPE,
23
+ stderr=asyncio.subprocess.PIPE
24
+ )
25
+
26
+ stdout, stderr = await proc.communicate()
27
+
28
+ # proc = subprocess.Popen(
29
+ # [sys.executable, '-m', 'egse.logger.log_cs', 'status'],
30
+ # stdout=subprocess.PIPE, stderr=subprocess.PIPE,
31
+ # )
32
+ #
33
+ # stdout, stderr = proc.communicate()
34
+
35
+ rich.print(stdout.decode(), end='')
36
+ if stderr:
37
+ rich.print(f"[red]{stderr.decode()}[/]")
38
+
39
+
40
+ async def status_sm_cs():
41
+
42
+ proc = await asyncio.create_subprocess_exec(
43
+ sys.executable, '-m', 'egse.storage.storage_cs', 'status',
44
+ stdout=asyncio.subprocess.PIPE,
45
+ stderr=asyncio.subprocess.PIPE
46
+ )
47
+
48
+ stdout, stderr = await proc.communicate()
49
+
50
+ # proc = subprocess.Popen(
51
+ # [sys.executable, '-m', 'egse.storage.storage_cs', 'status'],
52
+ # stdout=subprocess.PIPE, stderr=subprocess.PIPE,
53
+ # )
54
+ #
55
+ # stdout, stderr = proc.communicate()
56
+
57
+ rich.print(stdout.decode(), end='')
58
+ if stderr:
59
+ rich.print(f"[red]{stderr.decode()}[/]")
60
+
61
+
62
+ async def status_cm_cs():
63
+
64
+ proc = await asyncio.create_subprocess_exec(
65
+ sys.executable, '-m', 'egse.confman.confman_cs', 'status',
66
+ stdout=asyncio.subprocess.PIPE,
67
+ stderr=asyncio.subprocess.PIPE
68
+ )
69
+
70
+ stdout, stderr = await proc.communicate()
71
+
72
+ # proc = subprocess.Popen(
73
+ # [sys.executable, '-m', 'egse.confman.confman_cs', 'status'],
74
+ # stdout=subprocess.PIPE, stderr=subprocess.PIPE,
75
+ # )
76
+ #
77
+ # stdout, stderr = proc.communicate()
78
+
79
+ rich.print(stdout.decode(), end='')
80
+ if stderr:
81
+ rich.print(f"[red]{stderr.decode()}[/]")
scripts/_stop.py ADDED
@@ -0,0 +1,41 @@
1
+ import subprocess
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import rich
6
+
7
+
8
+ def stop_log_cs():
9
+ rich.print("Terminating the logging core service...")
10
+
11
+ out = open(Path('~/.log_cs.stop.out').expanduser(), 'w')
12
+
13
+ subprocess.Popen(
14
+ [sys.executable, '-m', 'egse.logger.log_cs', 'stop'],
15
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
16
+ close_fds=True
17
+ )
18
+
19
+
20
+ def stop_sm_cs():
21
+ rich.print("Terminating the storage manager core service...")
22
+
23
+ out = open(Path('~/.sm_cs.stop.out').expanduser(), 'w')
24
+
25
+ subprocess.Popen(
26
+ [sys.executable, '-m', 'egse.storage.storage_cs', 'stop'],
27
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
28
+ close_fds=True
29
+ )
30
+
31
+
32
+ def stop_cm_cs():
33
+ rich.print("Terminating the configuration manager core service...")
34
+
35
+ out = open(Path('~/.cm_cs.stop.out').expanduser(), 'w')
36
+
37
+ subprocess.Popen(
38
+ [sys.executable, '-m', 'egse.confman.confman_cs', 'stop'],
39
+ stdout=out, stderr=out, stdin=subprocess.DEVNULL,
40
+ close_fds=True
41
+ )
scripts/cgse.py CHANGED
@@ -9,37 +9,30 @@ $ cgse version
9
9
  Prints the installed version of the cgse-core and any other package that is registered under
10
10
  the entry points group 'cgse.version'.
11
11
 
12
- $ cgse {start,stop} {core,service} NAME ARGS
12
+ $ cgse core {start,stop,status}
13
13
 
14
- Starts, stops the core services or any service that is registered under the entry points
15
- group 'cgse.service.plugins'.
16
-
17
- $ cgse status {core,service} NAME ARGS
18
-
19
- Prints the status of the core services or any service that is registered under the entry
20
- points group 'cgse.service.plugins'.
14
+ Starts, stops, or prints the status of the core services.
21
15
 
22
16
  Other main commands can be added from external packages when they are provided as entry points with
23
- the group name 'cgse.plugins'. The entry point needs to be a Click command.
17
+ the group name 'cgse.command.plugins'.
24
18
 
19
+ Commands can be added as single commands or as a group containing further sub-commands. To add a group,
20
+ the entry point shall contain 'group' in its extras.
25
21
  """
26
- import subprocess
27
22
 
28
- import click
29
23
  import rich
24
+ import typer
25
+ from rich.console import Console
26
+ from rich.traceback import Traceback
30
27
 
31
28
  from egse.plugin import entry_points
32
- from egse.plugin import handle_click_plugins
33
- from egse.process import SubProcess
34
-
29
+ from scripts import services
35
30
 
36
- @handle_click_plugins(entry_points("cgse.plugins"))
37
- @click.group()
38
- def cli():
39
- pass
31
+ app = typer.Typer(add_completion=True)
32
+ app.add_typer(services.app, name="core")
40
33
 
41
34
 
42
- @cli.command()
35
+ @app.command()
43
36
  def version():
44
37
  """Prints the version of the cgse-core and other registered packages."""
45
38
  from egse.version import get_version_installed
@@ -52,128 +45,54 @@ def version():
52
45
  rich.print(f"{ep.name.upper()} installed version = [bold default]{installed_version}[/]")
53
46
 
54
47
 
55
- @cli.group()
56
- @click.pass_context
57
- def start(ctx):
58
- """Start the service"""
59
- ctx.ensure_object(dict)
60
-
61
- ctx.obj['action'] = 'start'
62
-
63
-
64
- @cli.group()
65
- @click.pass_context
66
- def stop(ctx):
67
- """Stop the service"""
68
- ctx.ensure_object(dict)
69
-
70
- ctx.obj['action'] = 'stop'
71
-
72
-
73
- @cli.group()
74
- @click.pass_context
75
- def status(ctx):
76
- """Provide the status of the service"""
77
- ctx.ensure_object(dict)
78
-
79
- ctx.obj['action'] = 'status'
80
-
81
-
82
- @start.command()
83
- @click.pass_context
84
- def core(ctx):
85
- print(f"executing: cgse {ctx.obj['action']} core")
86
- ctx.invoke(log_cs)
87
- ctx.invoke(sm_cs)
88
- ctx.invoke(cm_cs)
89
- ctx.invoke(pm_cs)
90
-
91
-
92
- @handle_click_plugins(entry_points("cgse.service.plugins"))
93
- @start.group()
94
- @click.pass_context
95
- def service(ctx):
96
- pass
97
-
98
-
99
- stop.add_command(core)
100
- stop.add_command(service)
101
-
102
- status.add_command(core)
103
- status.add_command(service)
104
-
105
-
106
- @service.command()
107
- @click.pass_context
108
- def log_cs(ctx):
109
- print(f"executing: log_cs {ctx.obj['action']}")
110
- if ctx.obj['action'] == 'start':
111
- proc = SubProcess("log_cs", ["log_cs", "start"])
112
- proc.execute()
113
- elif ctx.obj['action'] == 'stop':
114
- proc = SubProcess("log_cs", ["log_cs", "stop"])
115
- proc.execute()
116
- elif ctx.obj['action'] == 'status':
117
- proc = SubProcess("log_cs", ["log_cs", "status"], stdout=subprocess.PIPE)
118
- proc.execute()
119
- output, _ = proc.communicate()
120
- rich.print(output, end='')
121
- else:
122
- rich.print(f"[red]ERROR: Unknown action '{ctx.obj['action']}'[/]")
123
-
124
-
125
- @service.command()
126
- @click.pass_context
127
- def sm_cs(ctx):
128
- print(f"executing: sm_cs {ctx.obj['action']}")
129
- if ctx.obj['action'] == 'start':
130
- proc = SubProcess("sm_cs", ["sm_cs", "start"])
131
- proc.execute()
132
- elif ctx.obj['action'] == 'stop':
133
- proc = SubProcess("sm_cs", ["sm_cs", "stop"])
134
- proc.execute()
135
- elif ctx.obj['action'] == 'status':
136
- proc = SubProcess("sm_cs", ["sm_cs", "status"], stdout=subprocess.PIPE)
137
- proc.execute()
138
- output, _ = proc.communicate()
139
- rich.print(output, end='')
140
- else:
141
- rich.print(f"[red]ERROR: Unknown action '{ctx.obj['action']}'[/]")
142
-
143
-
144
- @service.command()
145
- @click.pass_context
146
- def cm_cs(ctx):
147
- print(f"executing: cm_cs {ctx.obj['action']}")
148
- if ctx.obj['action'] == 'start':
149
- proc = SubProcess("cm_cs", ["cm_cs", "start"])
150
- proc.execute()
151
- elif ctx.obj['action'] == 'stop':
152
- proc = SubProcess("cm_cs", ["cm_cs", "stop"])
153
- proc.execute()
154
- elif ctx.obj['action'] == 'status':
155
- proc = SubProcess("cm_cs", ["cm_cs", "status"], stdout=subprocess.PIPE)
156
- proc.execute()
157
- output, _ = proc.communicate()
158
- rich.print(output, end='')
159
- else:
160
- rich.print(f"[red]ERROR: Unknown action '{ctx.obj['action']}'[/]")
161
-
162
-
163
- @service.command()
164
- @click.pass_context
165
- def pm_cs(ctx):
166
- print(f"executing: pm_cs {ctx.obj['action']}")
167
- if ctx.obj['action'] == 'start':
168
- proc = SubProcess("pm_cs", ["pm_cs", "start"])
169
- proc.execute()
170
- elif ctx.obj['action'] == 'stop':
171
- proc = SubProcess("pm_cs", ["pm_cs", "stop"])
172
- proc.execute()
173
- elif ctx.obj['action'] == 'status':
174
- proc = SubProcess("pm_cs", ["pm_cs", "status"], stdout=subprocess.PIPE)
175
- proc.execute()
176
- output, _ = proc.communicate()
177
- rich.print(output, end='')
178
- else:
179
- rich.print(f"[red]ERROR: Unknown action '{ctx.obj['action']}'[/]")
48
+ def broken_command(name: str, module: str, exc: Exception):
49
+ """
50
+ Rather than completely crash the CLI when a broken plugin is loaded, this
51
+ function provides a modified help message informing the user that the plugin is
52
+ broken and could not be loaded. If the user executes the plugin and specifies
53
+ the `--traceback` option a traceback is reported showing the exception the
54
+ plugin loader encountered.
55
+ """
56
+ def broken_plugin(traceback: bool = False):
57
+ rich.print(f"[red]ERROR: Couldn't load this plugin command: {name} ⟶ reason: {exc}[/]")
58
+ if traceback:
59
+ console = Console(width=100)
60
+ tb = Traceback.from_exception(type(exc), exc, exc.__traceback__)
61
+ console.print(tb)
62
+
63
+ broken_plugin.__doc__ = f"ERROR: Couldn't load plugin '{name}' from {module}"
64
+ broken_plugin.__name__ = name
65
+ return broken_plugin
66
+
67
+
68
+ # Load the known plugins for the `cgse` command. Plugins are added as commands to the `cgse`.
69
+
70
+ for ep in entry_points("cgse.command.plugins"):
71
+ try:
72
+ if not ep.extras or 'command' in ep.extras:
73
+ app.command()(ep.load())
74
+ elif 'group' in ep.extras:
75
+ app.add_typer(ep.load(), name=ep.name)
76
+ else:
77
+ rich.print(f"\n[red]ERROR: don't know what to do with {ep.extras} for {ep.name}, command not added.[/]\n")
78
+ except Exception as exc:
79
+ app.command()(broken_command(ep.name, ep.module, exc))
80
+
81
+
82
+ for ep in entry_points("cgse.service.plugins"):
83
+ try:
84
+ app.add_typer(ep.load(), name=ep.name)
85
+ except Exception as exc:
86
+ app.command()(broken_command(ep.name, ep.module, exc))
87
+
88
+
89
+ @app.callback(no_args_is_help=True, invoke_without_command=True)
90
+ def main():
91
+ """
92
+ The main cgse command to inspect, configure, monitor the core services
93
+ and device control servers.
94
+ """
95
+
96
+
97
+ if __name__ == "__main__":
98
+ app()
scripts/services.py ADDED
@@ -0,0 +1,49 @@
1
+ import asyncio
2
+
3
+ import rich
4
+ import typer
5
+
6
+ app = typer.Typer(
7
+ name="core",
8
+ help="handle core services: start, stop, status",
9
+ no_args_is_help=True
10
+ )
11
+
12
+
13
+ @app.command(name="start")
14
+ def start_core_services():
15
+ """Start the core services in the background."""
16
+ from scripts._start import start_log_cs, start_sm_cs, start_cm_cs
17
+
18
+ rich.print("[green]Starting the core services...[/]")
19
+
20
+ start_log_cs()
21
+ start_sm_cs()
22
+ start_cm_cs()
23
+
24
+
25
+ @app.command(name="stop")
26
+ def stop_core_services():
27
+ """Stop the core services."""
28
+ from scripts._stop import stop_log_cs, stop_sm_cs, stop_cm_cs
29
+
30
+ rich.print("[green]Terminating the core services...[/]")
31
+
32
+ stop_cm_cs()
33
+ stop_sm_cs()
34
+ stop_log_cs()
35
+
36
+
37
+ @app.command(name="status")
38
+ def status_core_services():
39
+ """Print the status of the core services."""
40
+ # from scripts._status import status_log_cs, status_sm_cs, status_cm_cs
41
+
42
+ rich.print("[green]Status of the core services...[/]")
43
+
44
+ from scripts._status import run_all_status
45
+ asyncio.run(run_all_status())
46
+
47
+ # status_log_cs()
48
+ # status_sm_cs()
49
+ # status_cm_cs()