qlever 0.5.12__py3-none-any.whl → 0.5.17__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.

Potentially problematic release.


This version of qlever might be problematic. Click here for more details.

qlever/commands/start.py CHANGED
@@ -13,6 +13,110 @@ from qlever.log import log
13
13
  from qlever.util import is_qlever_server_alive, run_command
14
14
 
15
15
 
16
+ # Construct the command line based on the config file.
17
+ def construct_command(args) -> str:
18
+ start_cmd = (
19
+ f"{args.server_binary}"
20
+ f" -i {args.name}"
21
+ f" -j {args.num_threads}"
22
+ f" -p {args.port}"
23
+ f" -m {args.memory_for_queries}"
24
+ f" -c {args.cache_max_size}"
25
+ f" -e {args.cache_max_size_single_entry}"
26
+ f" -k {args.cache_max_num_entries}"
27
+ )
28
+
29
+ if args.timeout:
30
+ start_cmd += f" -s {args.timeout}"
31
+ if args.access_token:
32
+ start_cmd += f" -a {args.access_token}"
33
+ if args.only_pso_and_pos_permutations:
34
+ start_cmd += " --only-pso-and-pos-permutations"
35
+ if not args.use_patterns:
36
+ start_cmd += " --no-patterns"
37
+ if args.use_text_index == "yes":
38
+ start_cmd += " -t"
39
+ start_cmd += f" > {args.name}.server-log.txt 2>&1"
40
+ return start_cmd
41
+
42
+
43
+ # Kill existing server on the same port. Trust that StopCommand() works?
44
+ # Maybe return StopCommand().execute(args) and handle it with a try except?
45
+ def kill_existing_server(args) -> bool:
46
+ args.cmdline_regex = f"^ServerMain.* -p {args.port}"
47
+ args.no_containers = True
48
+ if not StopCommand().execute(args):
49
+ log.error("Stopping the existing server failed")
50
+ return False
51
+ log.info("")
52
+ return True
53
+
54
+
55
+ # Run the command in a container
56
+ def wrap_command_in_container(args, start_cmd) -> str:
57
+ if not args.server_container:
58
+ args.server_container = f"qlever.server.{args.name}"
59
+ start_cmd = Containerize().containerize_command(
60
+ start_cmd,
61
+ args.system,
62
+ "run -d --restart=unless-stopped",
63
+ args.image,
64
+ args.server_container,
65
+ volumes=[("$(pwd)", "/index")],
66
+ ports=[(args.port, args.port)],
67
+ working_directory="/index",
68
+ )
69
+ return start_cmd
70
+
71
+
72
+ # When running natively, check if the binary exists and works.
73
+ def check_binary(binary) -> bool:
74
+ try:
75
+ run_command(f"{binary} --help")
76
+ return True
77
+ except Exception as e:
78
+ log.error(
79
+ f'Running "{binary}" failed, '
80
+ f"set `--server-binary` to a different binary or "
81
+ f"set `--system to a container system`"
82
+ )
83
+ log.info("")
84
+ log.info(f"The error message was: {e}")
85
+ return False
86
+
87
+
88
+ # Set the index description.
89
+ def set_index_description(access_arg, port, desc) -> bool:
90
+ curl_cmd = (
91
+ f"curl -Gs http://localhost:{port}/api"
92
+ f' --data-urlencode "index-description={desc}"'
93
+ f" {access_arg} > /dev/null"
94
+ )
95
+ log.debug(curl_cmd)
96
+ try:
97
+ run_command(curl_cmd)
98
+ except Exception as e:
99
+ log.error(f"Setting the index description failed ({e})")
100
+ return False
101
+ return True
102
+
103
+
104
+ # Set the text description.
105
+ def set_text_description(access_arg, port, text_desc) -> bool:
106
+ curl_cmd = (
107
+ f"curl -Gs http://localhost:{port}/api"
108
+ f' --data-urlencode "text-description={text_desc}"'
109
+ f" {access_arg} > /dev/null"
110
+ )
111
+ log.debug(curl_cmd)
112
+ try:
113
+ run_command(curl_cmd)
114
+ except Exception as e:
115
+ log.error(f"Setting the text description failed ({e})")
116
+ return False
117
+ return True
118
+
119
+
16
120
  class StartCommand(QleverCommand):
17
121
  """
18
122
  Class for executing the `start` command.
@@ -22,22 +126,35 @@ class StartCommand(QleverCommand):
22
126
  pass
23
127
 
24
128
  def description(self) -> str:
25
- return ("Start the QLever server (requires that you have built "
26
- "an index with `qlever index` before)")
129
+ return (
130
+ "Start the QLever server (requires that you have built "
131
+ "an index with `qlever index` before)"
132
+ )
27
133
 
28
134
  def should_have_qleverfile(self) -> bool:
29
135
  return True
30
136
 
31
- def relevant_qleverfile_arguments(self) -> dict[str: list[str]]:
32
- return {"data": ["name", "description", "text_description"],
33
- "server": ["server_binary", "host_name", "port",
34
- "access_token", "memory_for_queries",
35
- "cache_max_size", "cache_max_size_single_entry",
36
- "cache_max_num_entries", "num_threads",
37
- "timeout", "only_pso_and_pos_permutations",
38
- "use_patterns", "use_text_index",
39
- "warmup_cmd"],
40
- "runtime": ["system", "image", "server_container"]}
137
+ def relevant_qleverfile_arguments(self) -> dict[str : list[str]]:
138
+ return {
139
+ "data": ["name", "description", "text_description"],
140
+ "server": [
141
+ "server_binary",
142
+ "host_name",
143
+ "port",
144
+ "access_token",
145
+ "memory_for_queries",
146
+ "cache_max_size",
147
+ "cache_max_size_single_entry",
148
+ "cache_max_num_entries",
149
+ "num_threads",
150
+ "timeout",
151
+ "only_pso_and_pos_permutations",
152
+ "use_patterns",
153
+ "use_text_index",
154
+ "warmup_cmd",
155
+ ],
156
+ "runtime": ["system", "image", "server_container"],
157
+ }
41
158
 
42
159
  def additional_arguments(self, subparser) -> None:
43
160
  # subparser.add_argument("--kill-existing-with-same-name",
@@ -46,16 +163,27 @@ class StartCommand(QleverCommand):
46
163
  # help="If a QLever server is already running "
47
164
  # "with the same name, kill it before "
48
165
  # "starting a new server")
49
- subparser.add_argument("--kill-existing-with-same-port",
50
- action="store_true",
51
- default=False,
52
- help="If a QLever server is already running "
53
- "on the same port, kill it before "
54
- "starting a new server")
55
- subparser.add_argument("--no-warmup",
56
- action="store_true",
57
- default=False,
58
- help="Do not execute the warmup command")
166
+ subparser.add_argument(
167
+ "--kill-existing-with-same-port",
168
+ action="store_true",
169
+ default=False,
170
+ help="If a QLever server is already running "
171
+ "on the same port, kill it before "
172
+ "starting a new server",
173
+ )
174
+ subparser.add_argument(
175
+ "--no-warmup",
176
+ action="store_true",
177
+ default=False,
178
+ help="Do not execute the warmup command",
179
+ )
180
+ subparser.add_argument(
181
+ "--run-in-foreground",
182
+ action="store_true",
183
+ default=False,
184
+ help="Run the server in the foreground "
185
+ "(default: run in the background with `nohup`)",
186
+ )
59
187
 
60
188
  def execute(self, args) -> bool:
61
189
  # Kill existing server with the same name if so desired.
@@ -70,84 +198,57 @@ class StartCommand(QleverCommand):
70
198
 
71
199
  # Kill existing server on the same port if so desired.
72
200
  if args.kill_existing_with_same_port:
73
- args.cmdline_regex = f"^ServerMain.* -p {args.port}"
74
- args.no_containers = True
75
- StopCommand().execute(args)
76
- log.info("")
201
+ if args.kill_existing_with_same_port and not kill_existing_server(
202
+ args
203
+ ):
204
+ return False
77
205
 
78
206
  # Construct the command line based on the config file.
79
- start_cmd = (f"{args.server_binary}"
80
- f" -i {args.name}"
81
- f" -j {args.num_threads}"
82
- f" -p {args.port}"
83
- f" -m {args.memory_for_queries}"
84
- f" -c {args.cache_max_size}"
85
- f" -e {args.cache_max_size_single_entry}"
86
- f" -k {args.cache_max_num_entries}")
87
- if args.timeout:
88
- start_cmd += f" -s {args.timeout}"
89
- if args.access_token:
90
- start_cmd += f" -a {args.access_token}"
91
- if args.only_pso_and_pos_permutations:
92
- start_cmd += " --only-pso-and-pos-permutations"
93
- if not args.use_patterns:
94
- start_cmd += " --no-patterns"
95
- if args.use_text_index == "yes":
96
- start_cmd += " -t"
97
- start_cmd += f" > {args.name}.server-log.txt 2>&1"
207
+ start_cmd = construct_command(args)
98
208
 
99
209
  # Run the command in a container (if so desired). Otherwise run with
100
- # `nohup` so that it keeps running after the shell is closed.
210
+ # `nohup` so that it keeps running after the shell is closed. With
211
+ # `--run-in-foreground`, run the server in the foreground.
101
212
  if args.system in Containerize.supported_systems():
102
- if not args.server_container:
103
- args.server_container = f"qlever.server.{args.name}"
104
- start_cmd = Containerize().containerize_command(
105
- start_cmd,
106
- args.system, "run -d --restart=unless-stopped",
107
- args.image,
108
- args.server_container,
109
- volumes=[("$(pwd)", "/index")],
110
- ports=[(args.port, args.port)],
111
- working_directory="/index")
213
+ start_cmd = wrap_command_in_container(args, start_cmd)
214
+ elif args.run_in_foreground:
215
+ start_cmd = f"{start_cmd}"
112
216
  else:
113
217
  start_cmd = f"nohup {start_cmd} &"
114
218
 
115
219
  # Show the command line.
116
220
  self.show(start_cmd, only_show=args.show)
117
221
  if args.show:
118
- return False
222
+ return True
119
223
 
120
224
  # When running natively, check if the binary exists and works.
121
225
  if args.system == "native":
122
- try:
123
- run_command(f"{args.server_binary} --help")
124
- except Exception as e:
125
- log.error(f"Running \"{args.server_binary}\" failed, "
126
- f"set `--server-binary` to a different binary or "
127
- f"set `--system to a container system`")
128
- log.info("")
129
- log.info(f"The error message was: {e}")
226
+ ret = check_binary(args.server_binary)
227
+ if not ret:
130
228
  return False
131
229
 
132
230
  # Check if a QLever server is already running on this port.
133
- port = args.port
134
- if is_qlever_server_alive(port):
135
- log.error(f"QLever server already running on port {port}")
231
+ endpoint_url = f"http://localhost:{args.port}"
232
+ if is_qlever_server_alive(endpoint_url):
233
+ log.error(f"QLever server already running on {endpoint_url}")
136
234
  log.info("")
137
- log.info("To kill the existing server, use `qlever stop` "
138
- "or `qlever start` with option "
139
- "--kill-existing-with-same-port`")
235
+ log.info(
236
+ "To kill the existing server, use `qlever stop` "
237
+ "or `qlever start` with option "
238
+ "--kill-existing-with-same-port`"
239
+ )
140
240
 
141
241
  # Show output of status command.
142
- args.cmdline_regex = f"^ServerMain.* -p *{port}"
242
+ args.cmdline_regex = f"^ServerMain.* -p *{args.port}"
143
243
  log.info("")
144
244
  StatusCommand().execute(args)
145
-
146
245
  return False
147
246
 
148
247
  # Remove already existing container.
149
- if args.system in Containerize.supported_systems() \
150
- and args.kill_existing_with_same_port:
248
+ if (
249
+ args.system in Containerize.supported_systems()
250
+ and args.kill_existing_with_same_port
251
+ ):
151
252
  try:
152
253
  run_command(f"{args.system} rm -f {args.server_container}")
153
254
  except Exception as e:
@@ -164,7 +265,10 @@ class StartCommand(QleverCommand):
164
265
 
165
266
  # Execute the command line.
166
267
  try:
167
- run_command(start_cmd)
268
+ process = run_command(
269
+ start_cmd,
270
+ use_popen=args.run_in_foreground,
271
+ )
168
272
  except Exception as e:
169
273
  log.error(f"Starting the QLever server failed ({e})")
170
274
  return False
@@ -172,48 +276,61 @@ class StartCommand(QleverCommand):
172
276
  # Tail the server log until the server is ready (note that the `exec`
173
277
  # is important to make sure that the tail process is killed and not
174
278
  # just the bash process).
175
- log.info(f"Follow {args.name}.server-log.txt until the server is ready"
176
- f" (Ctrl-C stops following the log, but not the server)")
279
+ if args.run_in_foreground:
280
+ log.info(
281
+ f"Follow {args.name}.server-log.txt as long as the server is"
282
+ f" running (Ctrl-C stops the server)"
283
+ )
284
+ else:
285
+ log.info(
286
+ f"Follow {args.name}.server-log.txt until the server is ready"
287
+ f" (Ctrl-C stops following the log, but NOT the server)"
288
+ )
177
289
  log.info("")
178
290
  tail_cmd = f"exec tail -f {args.name}.server-log.txt"
179
291
  tail_proc = subprocess.Popen(tail_cmd, shell=True)
180
- while not is_qlever_server_alive(port):
292
+ while not is_qlever_server_alive(endpoint_url):
181
293
  time.sleep(1)
182
294
 
183
- # Set the access token if specified.
184
- access_arg = f"--data-urlencode \"access-token={args.access_token}\""
295
+ # Set the description for the index and text.
296
+ access_arg = f'--data-urlencode "access-token={args.access_token}"'
185
297
  if args.description:
186
- desc = args.description
187
- curl_cmd = (f"curl -Gs http://localhost:{port}/api"
188
- f" --data-urlencode \"index-description={desc}\""
189
- f" {access_arg} > /dev/null")
190
- log.debug(curl_cmd)
191
- try:
192
- run_command(curl_cmd)
193
- except Exception as e:
194
- log.error(f"Setting the index description failed ({e})")
298
+ ret = set_index_description(
299
+ access_arg, args.port, args.description
300
+ )
301
+ if not ret:
302
+ return False
195
303
  if args.text_description:
196
- text_desc = args.text_description
197
- curl_cmd = (f"curl -Gs http://localhost:{port}/api"
198
- f" --data-urlencode \"text-description={text_desc}\""
199
- f" {access_arg} > /dev/null")
200
- log.debug(curl_cmd)
201
- try:
202
- run_command(curl_cmd)
203
- except Exception as e:
204
- log.error(f"Setting the text description failed ({e})")
304
+ ret = set_text_description(
305
+ access_arg, args.port, args.text_description
306
+ )
307
+ if not ret:
308
+ return False
205
309
 
206
310
  # Kill the tail process. NOTE: `tail_proc.kill()` does not work.
207
- tail_proc.terminate()
311
+ if not args.run_in_foreground:
312
+ tail_proc.terminate()
208
313
 
209
314
  # Execute the warmup command.
210
315
  if args.warmup_cmd and not args.no_warmup:
211
316
  log.info("")
212
- WarmupCommand().execute(args)
317
+ if not WarmupCommand().execute(args):
318
+ log.error("Warmup failed")
319
+ return False
213
320
 
214
321
  # Show cache stats.
215
- log.info("")
216
- args.detailed = False
217
- args.server_url = None
218
- CacheStatsCommand().execute(args)
322
+ if not args.run_in_foreground:
323
+ log.info("")
324
+ args.detailed = False
325
+ args.server_url = None
326
+ CacheStatsCommand().execute(args)
327
+
328
+ # With `--run-in-foreground`, wait until the server is stopped.
329
+ if args.run_in_foreground:
330
+ try:
331
+ process.wait()
332
+ except KeyboardInterrupt:
333
+ process.terminate()
334
+ tail_proc.terminate()
335
+
219
336
  return True
qlever/commands/status.py CHANGED
@@ -35,7 +35,7 @@ class StatusCommand(QleverCommand):
35
35
  f"the command line matches {args.cmdline_regex}"
36
36
  f" using Python's psutil library", only_show=args.show)
37
37
  if args.show:
38
- return False
38
+ return True
39
39
 
40
40
  # Show the results as a table.
41
41
  num_processes_found = 0
@@ -47,3 +47,4 @@ class StatusCommand(QleverCommand):
47
47
  num_processes_found += 1
48
48
  if num_processes_found == 0:
49
49
  print("No processes found")
50
+ return True
qlever/commands/stop.py CHANGED
@@ -1,15 +1,40 @@
1
1
  from __future__ import annotations
2
-
3
2
  import re
4
-
5
3
  import psutil
6
-
7
4
  from qlever.command import QleverCommand
8
5
  from qlever.commands.status import StatusCommand
9
6
  from qlever.containerize import Containerize
10
7
  from qlever.log import log
11
8
  from qlever.util import show_process_info
12
9
 
10
+ # try to kill the given process, return true iff it was killed successfully.
11
+ # the process_info is used for logging.
12
+ def stop_process(proc, pinfo):
13
+ try:
14
+ proc.kill()
15
+ log.info(f"Killed process {pinfo['pid']}")
16
+ return True
17
+ except Exception as e:
18
+ log.error(f"Could not kill process with PID "
19
+ f"{pinfo['pid']} ({e}) ... try to kill it "
20
+ f"manually")
21
+ log.info("")
22
+ show_process_info(proc, "", show_heading=True)
23
+ return False
24
+
25
+
26
+ # try to stop and remove container. return True iff it was stopped
27
+ # successfully. Gives log info accordingly.
28
+ def stop_container(server_container):
29
+ for container_system in Containerize.supported_systems():
30
+ if Containerize.stop_and_remove_container(
31
+ container_system, server_container):
32
+ log.info(f"{container_system.capitalize()} container with "
33
+ f"name \"{server_container}\" stopped "
34
+ f" and removed")
35
+ return True
36
+ return False
37
+
13
38
 
14
39
  class StopCommand(QleverCommand):
15
40
  """
@@ -20,7 +45,7 @@ class StopCommand(QleverCommand):
20
45
  pass
21
46
 
22
47
  def description(self) -> str:
23
- return ("Stop QLever server for a given datasedataset or port")
48
+ return "Stop QLever server for a given datasedataset or port"
24
49
 
25
50
  def should_have_qleverfile(self) -> bool:
26
51
  return True
@@ -49,55 +74,41 @@ class StopCommand(QleverCommand):
49
74
  f"\"{args.server_container}\"")
50
75
  self.show(description, only_show=args.show)
51
76
  if args.show:
52
- return False
77
+ return True
53
78
 
54
79
  # First check if there is container running and if yes, stop and remove
55
80
  # it (unless the user has specified `--no-containers`).
56
81
  if not args.no_containers:
57
- for container_system in Containerize.supported_systems():
58
- if Containerize.stop_and_remove_container(
59
- container_system, args.server_container):
60
- log.info(f"{container_system.capitalize()} container with "
61
- f"name \"{args.server_container}\" stopped "
62
- f" and removed")
63
- return True
82
+ if stop_container(args.server_container):
83
+ return True
64
84
 
65
85
  # Check if there is a process running on the server port using psutil.
66
- #
67
86
  # NOTE: On MacOS, some of the proc's returned by psutil.process_iter()
68
87
  # no longer exist when we try to access them, so we just skip them.
88
+ stop_process_results = []
69
89
  for proc in psutil.process_iter():
70
90
  try:
71
91
  pinfo = proc.as_dict(
72
- attrs=['pid', 'username', 'create_time',
73
- 'memory_info', 'cmdline'])
92
+ attrs=['pid', 'username', 'create_time',
93
+ 'memory_info', 'cmdline'])
74
94
  cmdline = " ".join(pinfo['cmdline'])
75
95
  except Exception as e:
76
96
  log.debug(f"Error getting process info: {e}")
97
+ return False
77
98
  if re.search(cmdline_regex, cmdline):
78
99
  log.info(f"Found process {pinfo['pid']} from user "
79
100
  f"{pinfo['username']} with command line: {cmdline}")
80
101
  log.info("")
81
- try:
82
- proc.kill()
83
- log.info(f"Killed process {pinfo['pid']}")
84
- except Exception as e:
85
- log.error(f"Could not kill process with PID "
86
- f"{pinfo['pid']} ({e}) ... try to kill it "
87
- f"manually")
88
- log.info("")
89
- show_process_info(proc, "", show_heading=True)
90
- return False
91
- return True
102
+ stop_process_results.append(stop_process(proc, pinfo))
103
+ if len(stop_process_results) > 0:
104
+ return all(stop_process_results)
92
105
 
93
- # No matching process found.
106
+ # If no matching process found, show a message and the output of the
107
+ # status command.
94
108
  message = "No matching process found" if args.no_containers else \
95
- "No matching process or container found"
109
+ "No matching process or container found"
96
110
  log.error(message)
97
-
98
- # Show output of status command.
99
111
  args.cmdline_regex = "^ServerMain.* -i [^ ]*"
100
112
  log.info("")
101
113
  StatusCommand().execute(args)
102
-
103
- return False
114
+ return True
@@ -58,7 +58,7 @@ class SystemInfoCommand(QleverCommand):
58
58
  # Say what the command is doing.
59
59
  self.show("Show system information and Qleverfile", only_show=args.show)
60
60
  if args.show:
61
- return False
61
+ return True
62
62
 
63
63
  # Show system information.
64
64
  show_heading("System Information")
qlever/commands/ui.py CHANGED
@@ -70,8 +70,10 @@ class UiCommand(QleverCommand):
70
70
  "\n".join(["Stop running containers", pull_cmd, run_cmd, exec_cmd]),
71
71
  only_show=args.show,
72
72
  )
73
- if qlever_is_running_in_container or args.show:
73
+ if qlever_is_running_in_container:
74
74
  return False
75
+ if args.show:
76
+ return True
75
77
 
76
78
  # Stop running containers.
77
79
  for container_system in Containerize.supported_systems():
qlever/commands/warmup.py CHANGED
@@ -30,7 +30,7 @@ class WarmupCommand(QleverCommand):
30
30
  # Show what the command is doing.
31
31
  self.show(args.warmup_cmd, only_show=args.show)
32
32
  if args.show:
33
- return False
33
+ return True
34
34
 
35
35
  # Execute the command.
36
36
  try:
qlever/qlever_main.py CHANGED
@@ -35,31 +35,38 @@ def main():
35
35
  log.info("")
36
36
  log.info(colored(f"Command: {args.command}", attrs=["bold"]))
37
37
  log.info("")
38
- command_object.execute(args)
38
+ commandWasSuccesful = command_object.execute(args)
39
39
  log.info("")
40
+ if not commandWasSuccesful:
41
+ exit(1)
40
42
  except KeyboardInterrupt:
41
43
  log.info("")
42
44
  log.info("Ctrl-C pressed, exiting ...")
43
45
  log.info("")
44
- exit(0)
46
+ exit(1)
45
47
  except Exception as e:
46
48
  # Check if it's a certain kind of `AttributeError` and give a hint in
47
49
  # that case.
48
50
  match_error = re.search(r"object has no attribute '(.+)'", str(e))
49
- match_trace = re.search(r"(qlever/commands/.+\.py)\", line (\d+)",
50
- traceback.format_exc())
51
+ match_trace = re.search(
52
+ r"(qlever/commands/.+\.py)\", line (\d+)", traceback.format_exc()
53
+ )
51
54
  if isinstance(e, AttributeError) and match_error and match_trace:
52
55
  attribute = match_error.group(1)
53
56
  trace_command = match_trace.group(1)
54
57
  trace_line = match_trace.group(2)
55
58
  log.error(f"{e} in `{trace_command}` at line {trace_line}")
56
59
  log.info("")
57
- log.info(f"Likely cause: you used `args.{attribute}`, but it was "
58
- f"neither defined in `relevant_qleverfile_arguments` "
59
- f"nor in `additional_arguments`")
60
+ log.info(
61
+ f"Likely cause: you used `args.{attribute}`, but it was "
62
+ f"neither defined in `relevant_qleverfile_arguments` "
63
+ f"nor in `additional_arguments`"
64
+ )
60
65
  log.info("")
61
- log.info(f"If you did not implement `{trace_command}` yourself, "
62
- f"please report this issue")
66
+ log.info(
67
+ f"If you did not implement `{trace_command}` yourself, "
68
+ f"please report this issue"
69
+ )
63
70
  log.info("")
64
71
  else:
65
72
  log.error(f"An unexpected error occurred: {e}")