robotframework-debug 4.3.4__tar.gz → 4.5.0__tar.gz

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 (38) hide show
  1. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/PKG-INFO +6 -1
  2. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/RobotDebug.py +1 -1
  3. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/cmdcompleter.py +125 -51
  4. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/debugcmd.py +13 -4
  5. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/globals.py +7 -0
  6. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/history_app.py +7 -3
  7. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/lexer.py +8 -7
  8. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/prompttoolkitcmd.py +4 -4
  9. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/robotkeyword.py +11 -6
  10. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/robotlib.py +3 -1
  11. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/sourcelines.py +6 -2
  12. robotframework-debug-4.5.0/RobotDebug/version.py +1 -0
  13. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/pyproject.toml +5 -4
  14. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/PKG-INFO +6 -1
  15. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/SOURCES.txt +1 -0
  16. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/requires.txt +1 -1
  17. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/setup.py +6 -13
  18. robotframework-debug-4.5.0/tests/debug.robot +4 -0
  19. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/tests/step.robot +1 -0
  20. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/tests/test_debuglibrary.py +1 -1
  21. robotframework-debug-4.3.4/RobotDebug/version.py +0 -1
  22. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/.coveragerc +0 -0
  23. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/.gitpod.yml +0 -0
  24. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/ChangeLog +0 -0
  25. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/CreateWheel.sh +0 -0
  26. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/LICENSE +0 -0
  27. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/MANIFEST.in +0 -0
  28. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/README.rst +0 -0
  29. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/__init__.py +0 -0
  30. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/shell.py +0 -0
  31. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/RobotDebug/styles.py +0 -0
  32. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/_config.yml +0 -0
  33. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/dependency_links.txt +0 -0
  34. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/entry_points.txt +0 -0
  35. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/not-zip-safe +0 -0
  36. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/robotframework_debug.egg-info/top_level.txt +0 -0
  37. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/setup.cfg +0 -0
  38. {robotframework-debug-4.3.4 → robotframework-debug-4.5.0}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-debug
3
- Version: 4.3.4
3
+ Version: 4.5.0
4
4
  Summary: RobotFramework debug shell
5
5
  Home-page: https://github.com/imbus/robotframework-debug/
6
6
  Author: René Rohner
@@ -22,10 +22,15 @@ Classifier: Programming Language :: Python :: 3.7
22
22
  Classifier: Programming Language :: Python :: 3.8
23
23
  Classifier: Programming Language :: Python :: 3.9
24
24
  Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
25
26
  Classifier: Topic :: Utilities
26
27
  Requires-Python: >=3.8.0
27
28
  Description-Content-Type: text/x-rst
28
29
  License-File: LICENSE
30
+ Requires-Dist: prompt-toolkit>=3.0.38
31
+ Requires-Dist: robotframework<8.0,>=5.0
32
+ Requires-Dist: pygments>=2.14.0
33
+ Requires-Dist: pyperclip>=1.8.2
29
34
 
30
35
  Debug Library for Robot Framework
31
36
  =================================
@@ -44,7 +44,7 @@ class Listener:
44
44
 
45
45
  path = attrs["source"]
46
46
  if path and Path(path).exists() and path not in self.source_files:
47
- self.source_files[path] = Path(path).open().readlines()
47
+ self.source_files[path] = Path(path).open().readlines() # noqa: SIM115
48
48
  lineno = attrs["lineno"]
49
49
  self.library.current_source_path = path
50
50
  self.library.current_source_line = lineno
@@ -8,6 +8,7 @@ from prompt_toolkit.document import Document
8
8
  from robot.libraries.BuiltIn import BuiltIn
9
9
  from robot.parsing.parser.parser import _tokens_to_statements
10
10
 
11
+ from .globals import IS_RF_7, KEYWORD_SEP
11
12
  from .lexer import get_robot_token, get_variable_token
12
13
  from .prompttoolkitcmd import PromptToolkitCmd
13
14
  from .robotkeyword import normalize_kw
@@ -68,7 +69,16 @@ class StatementInformation:
68
69
 
69
70
  def _find_token_at_cursor(self):
70
71
  for token in self.statement.tokens:
71
- if token.type in ["KEYWORD", "IF", "FOR", "ELSE", "ELSE IF"]:
72
+ if token.type in [
73
+ "KEYWORD",
74
+ "IF",
75
+ "FOR",
76
+ "ELSE",
77
+ "ELSE IF",
78
+ "TRY",
79
+ "WHILE",
80
+ "VAR",
81
+ ]: # TODO: Commented to get all tokens in debug. lets see the impact
72
82
  self.statement_type = token.type
73
83
  if (
74
84
  token.lineno == self.cursor_row + 1
@@ -90,6 +100,12 @@ class CmdCompleter(Completer):
90
100
  self.helps = helps
91
101
  self.libs = libs
92
102
  self.keywords = list(keywords)
103
+ self.keywords_catalog = {}
104
+ for keyword in self.keywords:
105
+ self.keywords_catalog[normalize_kw(keyword.name)] = keyword
106
+ self.keywords_catalog[
107
+ f"{normalize_kw(keyword.parent.name)}.{normalize_kw(keyword.name)}"
108
+ ] = keyword
93
109
  self.current_statement = None
94
110
  for name, display, display_meta in self.get_commands():
95
111
  self.names.append(name)
@@ -160,28 +176,28 @@ class CmdCompleter(Completer):
160
176
  )
161
177
 
162
178
  def _get_argument_completer(self, text):
163
- for keyword in self.keywords:
164
- if normalize_kw(keyword.name) == normalize_kw(
165
- self.current_statement.statement.keyword
166
- ) or f"{normalize_kw(keyword.parent.name)}.{normalize_kw(keyword.name)}" == normalize_kw(
167
- self.current_statement.statement.keyword
168
- ):
169
- data_tokens = self.current_statement.statement.data_tokens
170
- args = keyword.args
171
- set_named_args, set_pos_args = self.get_set_args(args, data_tokens)
172
- if set_named_args:
173
- yield from self.get_named_arg_completion(args, set_named_args, set_pos_args)
174
- else:
175
- yield from self.get_pos_arg_completion(args, set_pos_args)
179
+ keyword = self.keywords_catalog.get(
180
+ normalize_kw(self.current_statement.statement.keyword), None
181
+ )
182
+ if keyword:
183
+ data_tokens = self.current_statement.statement.data_tokens
184
+ args = keyword.args
185
+ set_named_args, set_pos_args = self.get_set_args(args, data_tokens)
186
+ if set_named_args:
187
+ yield from self.get_named_arg_completion(args, set_named_args, set_pos_args)
188
+ else:
189
+ yield from self.get_pos_arg_completion(args, set_pos_args)
176
190
 
177
- def get_pos_arg_completion(self, args, set_pos_args):
191
+ def get_pos_arg_completion(
192
+ self, args, set_pos_args
193
+ ): # TODO: here is an issue. if more positional args are set, than existing, named_only will be removed from proposal
178
194
  for index, arg in enumerate([*args.positional_or_named, *args.named_only]):
179
195
  if index + 1 > len(set_pos_args):
180
- suffix = "=" if arg in [*args.positional_or_named, *args.named_only] else ""
196
+ # suffix = "=" if arg in [*args.positional_or_named, *args.named_only] else ""
181
197
  yield Completion(
182
- f"{arg}",
198
+ f"{arg}=",
183
199
  0,
184
- display=f"{arg}{suffix}",
200
+ display=f"{arg}=",
185
201
  display_meta=str(args.defaults.get(arg, "")),
186
202
  )
187
203
 
@@ -222,9 +238,10 @@ class CmdCompleter(Completer):
222
238
  previous_token = statement_info.previous_token
223
239
  token = statement_info.token
224
240
  cursor_pos = statement_info.cursor_pos
225
-
226
- # variables, keyword, args = parse_keyword(text.strip())
227
- if "FOR".startswith(text):
241
+ self.cmd_repl.set_toolbar_key(statement_type, token, cursor_pos)
242
+ if text == "":
243
+ yield from []
244
+ elif "FOR".startswith(text):
228
245
  yield from [
229
246
  Completion(
230
247
  "FOR ${var} IN @{list}\n Log ${var}\nEND",
@@ -261,40 +278,97 @@ class CmdCompleter(Completer):
261
278
  display_meta="If-Statement as multi line",
262
279
  ),
263
280
  ]
281
+ elif "WHILE".startswith(text):
282
+ yield from [
283
+ Completion(
284
+ "WHILE <py-eval>\n Log body\nEND",
285
+ -len(text),
286
+ display="WHILE loop",
287
+ display_meta="While-Loop",
288
+ ),
289
+ Completion(
290
+ "WHILE <py-eval> limit=5 on_limit=pass\n Log body\nEND",
291
+ -len(text),
292
+ display="WHILE loop (with limit)",
293
+ display_meta="While-Loop with limit and pass when reached",
294
+ ),
295
+ ]
296
+ elif "TRY".startswith(text):
297
+ yield from [
298
+ Completion(
299
+ "TRY\n Some Keyword\nEXCEPT\n Error Handler\nEND",
300
+ -len(text),
301
+ display="TRY/EXCEPT Statement",
302
+ display_meta="Try/Except that catches any error",
303
+ ),
304
+ Completion(
305
+ "TRY\n Some Keyword\nEXCEPT ValueError: * type=GLOB AS ${error}\n Log ${error}",
306
+ -len(text),
307
+ display="TRY/EXCEPT Statement (specific error)",
308
+ display_meta="Try/Except that catches a specific error as ${error}",
309
+ ),
310
+ ]
311
+ elif IS_RF_7 and "VAR".startswith(text):
312
+ yield from [
313
+ Completion(
314
+ "VAR ${var} <value>",
315
+ -len(text),
316
+ display="VAR (simple)",
317
+ display_meta="Variable assignment",
318
+ ),
319
+ Completion(
320
+ "VAR ${var} <value> scope=Suite",
321
+ -len(text),
322
+ display="VAR (suite scope)",
323
+ display_meta="Suite Variable assignment",
324
+ ),
325
+ Completion(
326
+ "VAR ${var} <value> <value> separator=${SPACE}",
327
+ -len(text),
328
+ display="VAR (multiple values)",
329
+ display_meta="Variable assignment concatenate",
330
+ ),
331
+ ]
264
332
  elif re.fullmatch(r"style {2,}.*", text):
265
333
  yield from _get_style_completions(text.lower())
266
334
  elif text.startswith("*"):
267
335
  yield from self._get_resource_completions(text.lower())
268
336
  elif token:
269
- for var in list(get_variable_token([token])):
270
- if var.col_offset <= cursor_col <= var.end_col_offset:
271
- token = var
272
- cursor_pos = cursor_col - var.col_offset
273
- self.cmd_repl.set_toolbar_key(statement_type, token, cursor_pos)
274
- if token.type in ["ASSIGN", "VARIABLE"] or (
275
- token.type in ["KEYWORD", "ARGUMENT"]
276
- and re.fullmatch(r"[$&@]\{[^}]*}?", token.value)
277
- ):
278
- yield from [
279
- Completion(
280
- f"{var[:-1]}",
281
- -cursor_pos,
282
- display=var,
283
- display_meta=repr(val),
284
- )
285
- for var, val in BuiltIn().get_variables().items()
286
- if normalize_kw(var[1:]).startswith(normalize_kw(token.value[1:cursor_pos]))
287
- ]
288
- elif token.type == "KEYWORD":
289
- yield from self._get_command_completions(token.value.lower())
290
- elif cursor_pos == 1 and previous_token and previous_token.type == "KEYWORD":
291
- yield from self._get_command_completions(f"{previous_token.value.lower()} ")
292
- elif (
293
- token.type in ["SEPARATOR", "EOL"]
294
- and cursor_pos >= 2
295
- and statement_type == "KEYWORD"
296
- ):
297
- yield from self._get_argument_completer(token.value)
337
+ yield from self._get_keyword_completions(
338
+ cursor_col, cursor_pos, previous_token, statement_type, token
339
+ )
340
+
341
+ def _get_keyword_completions(
342
+ self, cursor_col, cursor_pos, previous_token, statement_type, token
343
+ ):
344
+ for var in list(get_variable_token([token])):
345
+ if var.col_offset <= cursor_col <= var.end_col_offset:
346
+ token = var
347
+ cursor_pos = cursor_col - var.col_offset
348
+ SEPARATOR_SIZE = 2
349
+ if token.type in ["ASSIGN", "VARIABLE"] or (
350
+ token.type in ["KEYWORD", "ARGUMENT"] and re.fullmatch(r"[$&@]\{[^}]*}?", token.value)
351
+ ):
352
+ yield from [
353
+ Completion(
354
+ var,
355
+ -cursor_pos,
356
+ display=var,
357
+ display_meta=repr(val),
358
+ )
359
+ for var, val in BuiltIn().get_variables().items()
360
+ if normalize_kw(var[1:]).startswith(normalize_kw(token.value[1:cursor_pos]))
361
+ ]
362
+ elif token.type == "KEYWORD":
363
+ yield from self._get_command_completions(token.value.lower())
364
+ elif cursor_pos == 1 and previous_token and previous_token.type == "KEYWORD":
365
+ yield from self._get_command_completions(f"{previous_token.value.lower()} ")
366
+ elif (
367
+ token.type in ["SEPARATOR", "EOL"]
368
+ and cursor_pos >= SEPARATOR_SIZE
369
+ and statement_type == "KEYWORD"
370
+ ):
371
+ yield from self._get_argument_completer(token.value)
298
372
 
299
373
 
300
374
  class KeywordAutoSuggestion(AutoSuggest):
@@ -304,7 +378,7 @@ class KeywordAutoSuggestion(AutoSuggest):
304
378
  def get_suggestion(self, buffer: Buffer, document: Document) -> Union[Suggestion, None]:
305
379
  text = document.text
306
380
  completions = [compl.text for compl in self.completer.get_completions(document, None)]
307
- last_word = text.split(" ")[-1]
381
+ last_word = KEYWORD_SEP.split(text)[-1]
308
382
  matches = [kw for kw in completions if kw.startswith(last_word)]
309
383
  matches.extend([kw for kw in completions if kw.lower().startswith(last_word.lower())])
310
384
  return Suggestion(matches[0][len(last_word) :] if matches else "")
@@ -8,11 +8,13 @@ from prompt_toolkit.styles import merge_styles
8
8
  from robot.api import logger
9
9
  from robot.errors import ExecutionFailed, HandlerExecutionFailed
10
10
  from robot.libraries.BuiltIn import BuiltIn
11
+ from robot.running import Keyword
12
+ from robot.running.context import _ExecutionContext
11
13
  from robot.running.signalhandler import STOP_SIGNAL_MONITOR
12
14
  from robot.variables import is_variable
13
15
 
14
16
  from .cmdcompleter import CmdCompleter, KeywordAutoSuggestion
15
- from .globals import context
17
+ from .globals import IS_RF_7, context
16
18
  from .lexer import HEADER_MATCHER
17
19
  from .prompttoolkitcmd import PromptToolkitCmd
18
20
  from .robotkeyword import (
@@ -100,7 +102,7 @@ Access https://github.com/imbus/robotframework-debug for more details.\
100
102
  self.run_robot_command(command)
101
103
 
102
104
  def run_robot_command(self, command):
103
- """Run command in robotframewrk environment."""
105
+ """Run command in robotframework environment."""
104
106
  if not command:
105
107
  return
106
108
  result = []
@@ -288,13 +290,13 @@ def run_command(dbg_cmd, command: str) -> List[Tuple[str, str]]:
288
290
  if len(test.body) > 1:
289
291
  start = time.monotonic()
290
292
  for kw in test.body:
291
- kw.run(ctx)
293
+ run_keyword(kw, ctx)
292
294
  dbg_cmd.last_keyword_exec_time = time.monotonic() - start
293
295
  return_val = None
294
296
  else:
295
297
  kw = test.body[0]
296
298
  start = time.monotonic()
297
- return_val = kw.run(ctx)
299
+ return_val = run_keyword(kw, ctx)
298
300
  dbg_cmd.last_keyword_exec_time = time.monotonic() - start
299
301
  assign = set(_get_assignments(test))
300
302
  if not assign and return_val is not None:
@@ -307,3 +309,10 @@ def run_command(dbg_cmd, command: str) -> List[Tuple[str, str]]:
307
309
  output.append(("#", f"{pure_var} = {val!r}"))
308
310
  return output
309
311
  return []
312
+
313
+
314
+ def run_keyword(keyword: Keyword, context: _ExecutionContext):
315
+ if IS_RF_7:
316
+ test = context.test or context.suite
317
+ return keyword.run(test, context)
318
+ return keyword.run(context)
@@ -1,5 +1,8 @@
1
+ import re
1
2
  from enum import Enum
2
3
 
4
+ from robot.version import get_version
5
+
3
6
 
4
7
  class SingletonContext:
5
8
  in_step_mode = False
@@ -20,3 +23,7 @@ class StepMode(str, Enum):
20
23
  OUT = "_OUT"
21
24
  CONTINUE = "_CONTINUE"
22
25
  STOP = "_STOP"
26
+
27
+
28
+ IS_RF_7 = int(get_version().split(".", 1)[0]) >= 7 # noqa: PLR2004
29
+ KEYWORD_SEP = re.compile(r"[ \t]{2,}|\t")
@@ -136,10 +136,14 @@ def get_history_content(his, pure_commands: bool = True):
136
136
  [
137
137
  e
138
138
  for e in dict.fromkeys(
139
- reversed([re.sub(r" {2,}", " " * 4, v).strip() for v in his.get_strings()])
139
+ reversed(
140
+ [
141
+ re.sub(r"(?:(?<![\n ])(?:[ \t]{2,}|\t))", " " * 4, v).strip()
142
+ for v in his.get_strings()
143
+ ]
144
+ )
140
145
  )
141
- if (not HEADER_MATCHER.match(e) and pure_commands)
142
- or (HEADER_MATCHER.match(e) and not pure_commands)
146
+ if bool(HEADER_MATCHER.match(e)) != pure_commands
143
147
  ]
144
148
  )
145
149
  )
@@ -1,8 +1,9 @@
1
1
  import re
2
2
  from pathlib import Path
3
+ from typing import ClassVar, Dict, List
3
4
 
4
5
  from pygments.lexer import Lexer
5
- from pygments.token import Token
6
+ from pygments.token import Token, _TokenType
6
7
  from robot.parsing import get_tokens
7
8
 
8
9
 
@@ -49,16 +50,15 @@ def get_variable_token(token_list):
49
50
  var_tokens = list(token.tokenize_variables())
50
51
  except Exception:
51
52
  var_tokens = [token]
52
- for v_token in var_tokens:
53
- yield v_token
53
+ yield from var_tokens
54
54
 
55
55
 
56
56
  class RobotFrameworkLocalLexer(Lexer):
57
57
  name = "RobotFramework"
58
58
  url = "http://robotframework.org"
59
- aliases = ["robotframework"]
60
- filenames = ["*.robot", "*.resource"]
61
- mimetypes = ["text/x-robotframework"]
59
+ aliases: ClassVar[List[str]] = ["robotframework"]
60
+ filenames: ClassVar[List[str]] = ["*.robot", "*.resource"]
61
+ mimetypes: ClassVar[List[str]] = ["text/x-robotframework"]
62
62
 
63
63
  # PYGMENTS_STANDARD_TYPES = {
64
64
  # Token: '',
@@ -152,7 +152,7 @@ class RobotFrameworkLocalLexer(Lexer):
152
152
  # Generic.Traceback: 'gt',
153
153
  # }
154
154
 
155
- ROBOT_TO_PYGMENTS = {
155
+ ROBOT_TO_PYGMENTS: ClassVar[Dict[str, _TokenType]] = {
156
156
  "HEADER": Token.Keyword.Namespace,
157
157
  "DEFINITION": Token.Name.Class,
158
158
  "SETTING HEADER": Token.Keyword.Namespace,
@@ -206,6 +206,7 @@ class RobotFrameworkLocalLexer(Lexer):
206
206
  "CONTINUE": Token.Keyword,
207
207
  "BREAK": Token.Keyword,
208
208
  "OPTION": Token.Keyword,
209
+ "VAR": Token.Keyword,
209
210
  "SEPARATOR": Token.Punctuation,
210
211
  "COMMENT": Token.Comment,
211
212
  "CONTINUATION": Token.Operator,
@@ -304,16 +304,15 @@ class BaseCmd(cmd.Cmd):
304
304
  if line is None:
305
305
  return None
306
306
 
307
- if line == "exit" or line == "EOF":
307
+ if line in ["exit", "EOF"]:
308
308
  line = "EOF"
309
309
  return True
310
310
 
311
311
  line = self.precmd(line)
312
312
  stop = self.onecmd(line)
313
- stop = self.postcmd(stop, line)
313
+ return self.postcmd(stop, line)
314
314
 
315
315
  # do not run 'EOF' command to avoid override 'lastcmd'
316
- return stop
317
316
 
318
317
  def cmdloop(self, intro=None):
319
318
  """Better command loop.
@@ -429,7 +428,8 @@ Type "help" for more information.\
429
428
  ),
430
429
  ]
431
430
  )
432
- # if self.toolbar_token_tuple[0]:
431
+ ## UNCOMMEND FOR DEBUGGING
432
+ # if self.toolbar_token_tuple:
433
433
  # base.extend(
434
434
  # [
435
435
  # ("class:bottom-toolbar-key", "STATEMENT: "),
@@ -1,4 +1,3 @@
1
- import re
2
1
  import tempfile
3
2
  from pathlib import Path
4
3
  from typing import Iterator, List, Tuple
@@ -6,13 +5,17 @@ from typing import Iterator, List, Tuple
6
5
  from robot.libdocpkg.model import KeywordDoc, LibraryDoc
7
6
  from robot.libraries.BuiltIn import BuiltIn
8
7
  from robot.parsing import get_model
9
- from robot.running import TestSuite, UserLibrary
8
+ from robot.running import TestSuite
9
+
10
+ try:
11
+ from robot.running import UserLibrary as ResourceFile
12
+ except ImportError:
13
+ from robot.running import ResourceFile
10
14
  from robot.variables.search import is_variable
11
15
 
16
+ from .globals import KEYWORD_SEP
12
17
  from .robotlib import ImportedLibraryDocBuilder, ImportedResourceDocBuilder, get_libs
13
18
 
14
- KEYWORD_SEP = re.compile(" +|\t")
15
-
16
19
  _lib_keywords_cache = {}
17
20
  _resource_keywords_cache = {}
18
21
  temp_resources = []
@@ -38,7 +41,7 @@ def parse_keyword(command) -> Tuple[List[str], str, List[str]]:
38
41
  def get_lib_keywords(library) -> List[KeywordDoc]:
39
42
  """Get keywords of imported library."""
40
43
  if library.name not in _lib_keywords_cache:
41
- if isinstance(library, UserLibrary):
44
+ if isinstance(library, ResourceFile):
42
45
  _lib_keywords_cache[library.name]: LibraryDoc = ImportedResourceDocBuilder().build(
43
46
  library
44
47
  )
@@ -104,9 +107,11 @@ def _import_resource_from_string(command):
104
107
  def _get_assignments(body_elem):
105
108
  if hasattr(body_elem, "assign"):
106
109
  yield from body_elem.assign
107
- else:
110
+ elif hasattr(body_elem, "body"):
108
111
  for child in body_elem.body:
109
112
  yield from _get_assignments(child)
113
+ elif body_elem.type == "VAR":
114
+ yield body_elem.name
110
115
 
111
116
 
112
117
  def run_debug_if(condition, *args):
@@ -1,3 +1,5 @@
1
+ from copy import deepcopy
2
+
1
3
  from robot.libdocpkg.model import LibraryDoc
2
4
  from robot.libdocpkg.robotbuilder import (
3
5
  KeywordDocBuilder,
@@ -44,7 +46,7 @@ class ImportedResourceDocBuilder(ResourceDocBuilder):
44
46
  type="RESOURCE",
45
47
  scope="GLOBAL",
46
48
  )
47
- libdoc.keywords = KeywordDocBuilder().build_keywords(resource)
49
+ libdoc.keywords = KeywordDocBuilder().build_keywords(deepcopy(resource))
48
50
  return libdoc
49
51
 
50
52
 
@@ -16,7 +16,9 @@ def print_source_lines(style, source_file, lineno, before_and_after=5):
16
16
  if not source_file or not lineno:
17
17
  return
18
18
 
19
- Path(source_file).open().readlines()
19
+ Path(
20
+ source_file
21
+ ).open().readlines() # noqa: SIM115 TODO: not sure why i did this. Maybe to check if file is actually readable...
20
22
  prefixed_token = get_pygments_token_from_file(lineno, source_file)
21
23
  printable_token = filter_token_by_lineno(
22
24
  prefixed_token, lineno - before_and_after, lineno + before_and_after + 1
@@ -28,7 +30,9 @@ def print_test_case_lines(style, source_file, current_lineno):
28
30
  if not source_file or not current_lineno:
29
31
  return
30
32
 
31
- Path(source_file).open().readlines()
33
+ Path(
34
+ source_file
35
+ ).open().readlines() # noqa: SIM115 TODO: not sure why i did this. Maybe to check if file is actually readable...
32
36
  prefixed_token = get_pygments_token_from_file(current_lineno, source_file)
33
37
  printable_token = filter_token_by_scope(prefixed_token, current_lineno)
34
38
  print_pygments_styles(printable_token, style)
@@ -0,0 +1 @@
1
+ VERSION = "4.5.0"
@@ -1,13 +1,14 @@
1
1
  [tool.black]
2
2
  target-version = ['py37']
3
- line-length = 120
3
+ line-length = 100
4
4
 
5
5
  [tool.ruff]
6
- unfixable = []
6
+ lint.unfixable = []
7
7
  exclude = [
8
8
  "__pycache__",
9
+ "tests/"
9
10
  ]
10
- ignore = [
11
+ lint.ignore = [
11
12
  "B008", # do not perform function calls in argument defaults
12
13
  "E501", # line too long
13
14
  "N815", # mixedCase variable in class scope
@@ -18,7 +19,7 @@ ignore = [
18
19
  "PLR0913", # too many arguments
19
20
  ]
20
21
  target-version = "py37"
21
- select = [
22
+ lint.select = [
22
23
  "E",
23
24
  "F",
24
25
  "W",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-debug
3
- Version: 4.3.4
3
+ Version: 4.5.0
4
4
  Summary: RobotFramework debug shell
5
5
  Home-page: https://github.com/imbus/robotframework-debug/
6
6
  Author: René Rohner
@@ -22,10 +22,15 @@ Classifier: Programming Language :: Python :: 3.7
22
22
  Classifier: Programming Language :: Python :: 3.8
23
23
  Classifier: Programming Language :: Python :: 3.9
24
24
  Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
25
26
  Classifier: Topic :: Utilities
26
27
  Requires-Python: >=3.8.0
27
28
  Description-Content-Type: text/x-rst
28
29
  License-File: LICENSE
30
+ Requires-Dist: prompt-toolkit>=3.0.38
31
+ Requires-Dist: robotframework<8.0,>=5.0
32
+ Requires-Dist: pygments>=2.14.0
33
+ Requires-Dist: pyperclip>=1.8.2
29
34
 
30
35
  Debug Library for Robot Framework
31
36
  =================================
@@ -31,5 +31,6 @@ robotframework_debug.egg-info/not-zip-safe
31
31
  robotframework_debug.egg-info/requires.txt
32
32
  robotframework_debug.egg-info/top_level.txt
33
33
  tests/__init__.py
34
+ tests/debug.robot
34
35
  tests/step.robot
35
36
  tests/test_debuglibrary.py
@@ -1,4 +1,4 @@
1
1
  prompt-toolkit>=3.0.38
2
- robotframework>=5.0
2
+ robotframework<8.0,>=5.0
3
3
  pygments>=2.14.0
4
4
  pyperclip>=1.8.2
@@ -1,21 +1,13 @@
1
1
  #!/usr/bin/env python
2
2
 
3
- import io
4
- import os
5
3
  import re
4
+ from pathlib import Path
6
5
 
7
6
  from setuptools import setup
8
7
 
9
- ROOT = os.path.abspath(os.path.dirname(__file__))
10
8
 
11
-
12
- def read(*names, **kwargs):
13
- with io.open(os.path.join(ROOT, *names), encoding=kwargs.get("encoding", "utf8")) as fp:
14
- return fp.read()
15
-
16
-
17
- def find_version(*file_paths):
18
- version_file = read(*file_paths)
9
+ def find_version(file_paths):
10
+ version_file = Path(file_paths).read_text()
19
11
  version_match = re.search(r"^VERSION = ['\"]([^'\"]*)['\"]", version_file, re.M)
20
12
  if version_match:
21
13
  return version_match.group(1)
@@ -26,7 +18,7 @@ setup(
26
18
  name="robotframework-debug",
27
19
  version=find_version("RobotDebug/version.py"),
28
20
  description="RobotFramework debug shell",
29
- long_description=read("README.rst"),
21
+ long_description=Path("README.rst").read_text(),
30
22
  long_description_content_type=("text/x-rst"),
31
23
  author="René Rohner",
32
24
  author_email="snooz@postoe.de",
@@ -43,7 +35,7 @@ setup(
43
35
  keywords="robotframework,debug,shell,repl",
44
36
  install_requires=[
45
37
  "prompt-toolkit >= 3.0.38",
46
- "robotframework >= 5.0",
38
+ "robotframework >= 5.0, < 8.0",
47
39
  "pygments >= 2.14.0",
48
40
  "pyperclip >= 1.8.2",
49
41
  ],
@@ -63,6 +55,7 @@ setup(
63
55
  "Programming Language :: Python :: 3.8",
64
56
  "Programming Language :: Python :: 3.9",
65
57
  "Programming Language :: Python :: 3.10",
58
+ "Programming Language :: Python :: 3.11",
66
59
  "Topic :: Utilities",
67
60
  ],
68
61
  )
@@ -0,0 +1,4 @@
1
+ *** Test Cases ***
2
+ Test
3
+ Import Resource ${CURDIR}/keyword.resource
4
+ kw
@@ -1,5 +1,6 @@
1
1
  *** Settings ***
2
2
  Library RobotDebug
3
+ Suite Setup debug
3
4
 
4
5
  *** Test Cases ***
5
6
  test1
@@ -153,7 +153,7 @@ def step_functional_testing():
153
153
  check_command("", "=> BuiltIn.Log To Console another test case") # repeat last command
154
154
 
155
155
  # Exit the debug mode started by Debug keyword.
156
- check_command("c", "Exit shell.*" "another test case.*" "end") # continue
156
+ check_command("c", "Exit shell.*another test case.*end") # continue
157
157
  # Exit the interactive shell started by "DebugLibrary/shell.py".
158
158
  check_command("c", "Report: ")
159
159
  child.wait()
@@ -1 +0,0 @@
1
- VERSION = "4.3.4"