httpdbg 0.29.0__tar.gz → 0.29.2__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 (76) hide show
  1. {httpdbg-0.29.0 → httpdbg-0.29.2}/PKG-INFO +1 -1
  2. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/__init__.py +1 -1
  3. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/__main__.py +1 -1
  4. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/aiohttp.py +5 -4
  5. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/httpx.py +10 -8
  6. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/requests.py +5 -4
  7. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/urllib3.py +5 -4
  8. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/initiator.py +28 -17
  9. httpdbg-0.29.2/httpdbg/mode_console.py +81 -0
  10. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/records.py +3 -2
  11. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/api.py +6 -1
  12. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/api.js +1 -0
  13. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/index.htm +2 -2
  14. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/PKG-INFO +1 -1
  15. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_mode_console.py +29 -1
  16. httpdbg-0.29.0/httpdbg/mode_console.py +0 -37
  17. {httpdbg-0.29.0 → httpdbg-0.29.2}/LICENSE +0 -0
  18. {httpdbg-0.29.0 → httpdbg-0.29.2}/README.md +0 -0
  19. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/args.py +0 -0
  20. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/env.py +0 -0
  21. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/exception.py +0 -0
  22. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/__init__.py +0 -0
  23. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/all.py +0 -0
  24. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/external.py +0 -0
  25. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/generic.py +0 -0
  26. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/pytest.py +0 -0
  27. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/socket.py +0 -0
  28. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/hooks/utils.py +0 -0
  29. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/log.py +0 -0
  30. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/mode_module.py +0 -0
  31. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/mode_script.py +0 -0
  32. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/preview.py +0 -0
  33. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/server.py +0 -0
  34. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/utils.py +0 -0
  35. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/__init__.py +0 -0
  36. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/app.py +0 -0
  37. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/column.css +0 -0
  38. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/configuration.js +0 -0
  39. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/detail.css +0 -0
  40. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/favicon.ico +0 -0
  41. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/filter.js +0 -0
  42. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/chevron-direction-top-outline-icon.svg +0 -0
  43. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/download-install-line-icon.svg +0 -0
  44. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/engine-gears-icon.svg +0 -0
  45. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/files-papers-outline-icon.svg +0 -0
  46. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/filter-outline-icon.svg +0 -0
  47. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/information-mark-circle-outline-icon.svg +0 -0
  48. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/math-multiplication-icon.svg +0 -0
  49. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/push-pin-green-icon.svg +0 -0
  50. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/question-mark-round-line-icon.svg +0 -0
  51. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/recycle-bin-line-icon.svg +0 -0
  52. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/icons/wait-sandclock-icon.svg +0 -0
  53. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/main.js +0 -0
  54. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/mustache.min.js +0 -0
  55. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/render.js +0 -0
  56. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/style.css +0 -0
  57. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg/webapp/static/table.css +0 -0
  58. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/SOURCES.txt +0 -0
  59. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/dependency_links.txt +0 -0
  60. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/entry_points.txt +0 -0
  61. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/requires.txt +0 -0
  62. {httpdbg-0.29.0 → httpdbg-0.29.2}/httpdbg.egg-info/top_level.txt +0 -0
  63. {httpdbg-0.29.0 → httpdbg-0.29.2}/pyproject.toml +0 -0
  64. {httpdbg-0.29.0 → httpdbg-0.29.2}/setup.cfg +0 -0
  65. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_api.py +0 -0
  66. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_cli.py +0 -0
  67. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_context_manager.py +0 -0
  68. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_hook_aiohttp.py +0 -0
  69. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_hook_httpx.py +0 -0
  70. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_hook_requests.py +0 -0
  71. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_hook_urllib3.py +0 -0
  72. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_initiator.py +0 -0
  73. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_mode_module_pytest.py +0 -0
  74. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_mode_script.py +0 -0
  75. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_preview.py +0 -0
  76. {httpdbg-0.29.0 → httpdbg-0.29.2}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.29.0
3
+ Version: 0.29.2
4
4
  Summary: A very simple tool to debug HTTP(S) client requests
5
5
  Author-email: cle-b <cle@tictac.pm>
6
6
  License: Apache-2.0
@@ -3,6 +3,6 @@ from httpdbg.hooks.all import httprecord
3
3
  from httpdbg.records import HTTPRecords
4
4
 
5
5
 
6
- __version__ = "0.29.0"
6
+ __version__ = "0.29.2"
7
7
 
8
8
  __all__ = ["httprecord", "HTTPRecords"]
@@ -35,7 +35,7 @@ def pyhttpdbg(params, subparams, test_mode=False):
35
35
  elif params.script:
36
36
  run_script(subparams)
37
37
  else:
38
- run_console(test_mode)
38
+ run_console(records, test_mode)
39
39
 
40
40
  if not (params.force_quit or test_mode):
41
41
  print_msg(f" httpdbg - HTTP(S) requests available at {url}")
@@ -13,20 +13,21 @@ from httpdbg.records import HTTPRecords
13
13
 
14
14
  def set_hook_for_aiohttp_async(records: HTTPRecords, method: Callable):
15
15
  async def hook(*args, **kwargs):
16
- initiator = None
16
+ initiator_and_group = None
17
17
  try:
18
18
  with httpdbg_initiator(
19
19
  records, traceback.extract_stack(), method, *args, **kwargs
20
- ) as initiator:
20
+ ) as initiator_and_group:
21
21
  ret = await method(*args, **kwargs)
22
22
  return ret
23
23
  except Exception as ex:
24
24
  callargs = getcallargs(method, *args, **kwargs)
25
25
 
26
26
  if "str_or_url" in callargs:
27
- if initiator:
27
+ if initiator_and_group:
28
+ initiator, group = initiator_and_group
28
29
  records.add_new_record_exception(
29
- initiator, str(callargs["str_or_url"]), ex
30
+ initiator, group, str(callargs["str_or_url"]), ex
30
31
  )
31
32
  raise
32
33
 
@@ -13,20 +13,21 @@ from httpdbg.records import HTTPRecords
13
13
 
14
14
  def set_hook_for_httpx_async(records: HTTPRecords, method: Callable):
15
15
  async def hook(*args, **kwargs):
16
- initiator = None
16
+ initiator_and_group = None
17
17
  try:
18
18
  with httpdbg_initiator(
19
19
  records, traceback.extract_stack(), method, *args, **kwargs
20
- ) as initiator:
20
+ ) as initiator_and_group:
21
21
  ret = await method(*args, **kwargs)
22
22
  return ret
23
23
  except Exception as ex:
24
24
  callargs = getcallargs(method, *args, **kwargs)
25
25
 
26
26
  if "url" in callargs:
27
- if initiator:
27
+ if initiator_and_group:
28
+ initiator, group = initiator_and_group
28
29
  records.add_new_record_exception(
29
- initiator, str(callargs["url"]), ex
30
+ initiator, group, str(callargs["url"]), ex
30
31
  )
31
32
  raise
32
33
 
@@ -35,20 +36,21 @@ def set_hook_for_httpx_async(records: HTTPRecords, method: Callable):
35
36
 
36
37
  def set_hook_for_httpx(records: HTTPRecords, method: Callable):
37
38
  def hook(*args, **kwargs):
38
- initiator = None
39
+ initiator_and_group = None
39
40
  try:
40
41
  with httpdbg_initiator(
41
42
  records, traceback.extract_stack(), method, *args, **kwargs
42
- ) as initiator:
43
+ ) as initiator_and_group:
43
44
  ret = method(*args, **kwargs)
44
45
  return ret
45
46
  except Exception as ex:
46
47
  callargs = getcallargs(method, *args, **kwargs)
47
48
 
48
49
  if "url" in callargs:
49
- if initiator:
50
+ if initiator_and_group:
51
+ initiator, group = initiator_and_group
50
52
  records.add_new_record_exception(
51
- initiator, str(callargs["url"]), ex
53
+ initiator, group, str(callargs["url"]), ex
52
54
  )
53
55
  raise
54
56
 
@@ -13,20 +13,21 @@ from httpdbg.records import HTTPRecords
13
13
 
14
14
  def set_hook_for_requests(records: HTTPRecords, method: Callable):
15
15
  def hook(*args, **kwargs):
16
- initiator = None
16
+ initiator_and_group = None
17
17
  try:
18
18
  with httpdbg_initiator(
19
19
  records, traceback.extract_stack(), method, *args, **kwargs
20
- ) as initiator:
20
+ ) as initiator_and_group:
21
21
  ret = method(*args, **kwargs)
22
22
  return ret
23
23
  except Exception as ex:
24
24
  callargs = getcallargs(method, *args, **kwargs)
25
25
 
26
26
  if "url" in callargs:
27
- if initiator:
27
+ if initiator_and_group:
28
+ initiator, group = initiator_and_group
28
29
  records.add_new_record_exception(
29
- initiator, str(callargs["url"]), ex
30
+ initiator, group, str(callargs["url"]), ex
30
31
  )
31
32
  raise
32
33
 
@@ -13,20 +13,21 @@ from httpdbg.records import HTTPRecords
13
13
 
14
14
  def set_hook_for_urllib3(records: HTTPRecords, method: Callable):
15
15
  def hook(*args, **kwargs):
16
- initiator = None
16
+ initiator_and_group = None
17
17
  try:
18
18
  with httpdbg_initiator(
19
19
  records, traceback.extract_stack(), method, *args, **kwargs
20
- ) as initiator:
20
+ ) as initiator_and_group:
21
21
  ret = method(*args, **kwargs)
22
22
  return ret
23
23
  except Exception as ex:
24
24
  callargs = getcallargs(method, *args, **kwargs)
25
25
 
26
26
  if "url" in callargs:
27
- if initiator:
27
+ if initiator_and_group:
28
+ initiator, group = initiator_and_group
28
29
  records.add_new_record_exception(
29
- initiator, str(callargs["url"]), ex
30
+ initiator, group, str(callargs["url"]), ex
30
31
  )
31
32
  raise
32
33
 
@@ -23,12 +23,11 @@ from httpdbg.log import logger
23
23
  class Initiator:
24
24
  def __init__(
25
25
  self,
26
- id: str,
27
26
  label: str,
28
27
  short_stack: str,
29
28
  stack: List[str],
30
29
  ):
31
- self.id = id
30
+ self.id = get_new_uuid()
32
31
  self.label = label
33
32
  self.short_stack = short_stack
34
33
  self.stack = stack
@@ -61,10 +60,10 @@ class Initiator:
61
60
 
62
61
 
63
62
  class Group:
64
- def __init__(self, id: str, label: str, full_label: str):
65
- self.id = id
66
- self.label = label
67
- self.full_label = full_label
63
+ def __init__(self, label: str, full_label: str):
64
+ self.id: str = get_new_uuid()
65
+ self.label: str = label
66
+ self.full_label: str = full_label
68
67
 
69
68
  def to_json(self) -> dict:
70
69
  return {"id": self.id, "label": self.label, "full_label": self.full_label}
@@ -195,7 +194,7 @@ def extract_short_stack_from_file(
195
194
  @contextmanager
196
195
  def httpdbg_initiator(
197
196
  records, extracted_stack: traceback.StackSummary, original_method, *args, **kwargs
198
- ) -> Generator[Union[Initiator, None], None, None]:
197
+ ) -> Generator[Union[Tuple[Initiator, Group], None], None, None]:
199
198
  envname = f"{HTTPDBG_CURRENT_INITIATOR}_{records.id}"
200
199
 
201
200
  if not os.environ.get(envname):
@@ -215,7 +214,7 @@ def httpdbg_initiator(
215
214
  short_stack += f" {k}={v}\n"
216
215
  short_stack += ")"
217
216
 
218
- current_initiator = Initiator(get_new_uuid(), instruction, short_stack, stack)
217
+ current_initiator = Initiator(instruction, short_stack, stack)
219
218
  records.initiators[current_initiator.id] = current_initiator
220
219
 
221
220
  os.environ[envname] = current_initiator.id
@@ -223,8 +222,8 @@ def httpdbg_initiator(
223
222
  try:
224
223
  with httpdbg_group(
225
224
  records, instruction, short_stack
226
- ): # by default we group the requests by initiator
227
- yield current_initiator
225
+ ) as group: # by default we group the requests by initiator
226
+ yield current_initiator, group
228
227
  except Exception:
229
228
  del os.environ[envname]
230
229
  raise
@@ -258,21 +257,33 @@ def httpdbg_tag(tag: str) -> Generator[None, None, None]:
258
257
  @contextmanager
259
258
  def httpdbg_group(
260
259
  records: "HTTPRecords", label: str, full_label: str
261
- ) -> Generator[None, None, None]:
262
-
263
- group_already_set = HTTPDBG_CURRENT_GROUP in os.environ
260
+ ) -> Generator[Group, None, None]:
261
+
262
+ # A group is considered set if the environment variable exists and the group
263
+ # is recorded.If the environment variable exists but the group is not recorded,
264
+ # it means httpdbg was called reentrantly, and we need to record the group again
265
+ # using the same id.
266
+ group_already_set = (HTTPDBG_CURRENT_GROUP in os.environ) and (
267
+ os.environ[HTTPDBG_CURRENT_GROUP] in records.groups
268
+ )
264
269
 
265
270
  if not group_already_set:
266
271
  logger().info("httpdbg_group (new)")
267
- group_id = get_new_uuid()
268
- records.groups[group_id] = Group(group_id, label, full_label)
269
- os.environ[HTTPDBG_CURRENT_GROUP] = group_id
272
+ group = Group(label, full_label)
273
+ # in case of a reentrant call to httpdbg, we force the group id to be the same
274
+ if HTTPDBG_CURRENT_GROUP in os.environ:
275
+ group.id = os.environ[HTTPDBG_CURRENT_GROUP]
276
+ logger().info("httpdbg_group (reentrant) keep id {group.id}")
277
+ records.groups[group.id] = group
278
+ os.environ[HTTPDBG_CURRENT_GROUP] = group.id
279
+ else:
280
+ group = records.groups[os.environ[HTTPDBG_CURRENT_GROUP]]
270
281
 
271
282
  try:
272
283
  logger().info(
273
284
  f"httpdbg_group {records.groups} group_id={os.environ[HTTPDBG_CURRENT_GROUP]} label={label} full_label={full_label}"
274
285
  )
275
- yield
286
+ yield group
276
287
  except Exception:
277
288
  if (not group_already_set) and (HTTPDBG_CURRENT_GROUP in os.environ):
278
289
  del os.environ[HTTPDBG_CURRENT_GROUP]
@@ -0,0 +1,81 @@
1
+ # -*- coding: utf-8 -*-
2
+ import code
3
+ from typing import List
4
+ from typing import Union
5
+
6
+ from httpdbg.records import HTTPRecords
7
+ from httpdbg.initiator import httpdbg_group
8
+
9
+
10
+ def console_exit():
11
+ raise SystemExit
12
+
13
+
14
+ class InteractiveConsoleWithHistory(code.InteractiveConsole):
15
+
16
+ def __init__(self, records: HTTPRecords, locals=None):
17
+ self.records: HTTPRecords = records
18
+ self.history: List[str] = []
19
+ self.incomplete_block: bool = False
20
+ super().__init__(locals)
21
+
22
+ def push(self, line):
23
+ if line: # if the line is empty, we don't add it in the history
24
+ if (
25
+ self.incomplete_block
26
+ ): # if the latest instruction is part of a block, we group it with the previous one
27
+ self.history[-1] += (
28
+ "\n " + line
29
+ ) # to simulate the block indentation
30
+ else:
31
+ self.history.append(line)
32
+
33
+ # if the latest instruction is part of a block, the label is generic
34
+ label = self.history[-1] if not self.incomplete_block else "(block)"
35
+
36
+ # in the full label, we keep the last 5 instructions/blocks
37
+ latest_history = self.history[-5:]
38
+ if not self.incomplete_block:
39
+ # in case of a block, we don't know which line within the block is the initiator.
40
+ # this is why we do not mark it in that case.
41
+ # this information is displayed in the initiator tooltip of the requests (in console, line X,)
42
+ latest_history[-1] += " <===="
43
+
44
+ full_label = ">>> " + "\n>>> ".join(latest_history)
45
+
46
+ with httpdbg_group(self.records, label, full_label):
47
+ self.incomplete_block = super().push(line)
48
+
49
+ return self.incomplete_block
50
+
51
+
52
+ def run_console(
53
+ records: HTTPRecords, test_mode: bool = False
54
+ ) -> Union[InteractiveConsoleWithHistory, None]:
55
+ try:
56
+ vars = globals()
57
+ vars.update(locals())
58
+
59
+ try:
60
+ # adds history and autocompletion capabilities
61
+ import readline
62
+ import rlcompleter
63
+
64
+ readline.set_completer(rlcompleter.Completer(vars).complete)
65
+ readline.parse_and_bind("tab: complete")
66
+ except ImportError: # pragma: no cover
67
+ # readline is not available on Windows
68
+ pass
69
+
70
+ vars.update({"exit": console_exit})
71
+
72
+ new_console = InteractiveConsoleWithHistory(records, vars)
73
+ if not test_mode:
74
+ new_console.interact() # pragma: no cover
75
+ else:
76
+ new_console.push("print('test_mode is on')")
77
+ return new_console
78
+ except SystemExit: # pragma: no cover
79
+ pass
80
+
81
+ return None
@@ -330,7 +330,7 @@ class HTTPRecords:
330
330
  stack.append(line)
331
331
  long_label = stack[-1]
332
332
  label = long_label.split("\n")[1]
333
- initiator = Initiator(get_new_uuid(), label, long_label, stack)
333
+ initiator = Initiator(label, long_label, stack)
334
334
 
335
335
  if initiator.id not in self.initiators:
336
336
  self.initiators[initiator.id] = initiator
@@ -410,13 +410,14 @@ class HTTPRecords:
410
410
  del self._sockets[id(obj)]
411
411
 
412
412
  def add_new_record_exception(
413
- self, initiator: Initiator, url: str, exception: Exception
413
+ self, initiator: Initiator, group: Group, url: str, exception: Exception
414
414
  ) -> HTTPRecord:
415
415
  if initiator.id not in self.initiators:
416
416
  self.initiators[initiator.id] = initiator
417
417
  new_record = HTTPRecord()
418
418
  new_record.url = url
419
419
  new_record.initiator_id = initiator.id
420
+ new_record.group_id = group.id
420
421
  new_record.exception = exception
421
422
  self.requests[new_record.id] = new_record
422
423
  return new_record
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  from json import JSONEncoder
3
+ import traceback
3
4
  from typing import Any
4
5
  from typing import Dict
5
6
 
@@ -53,7 +54,11 @@ class RequestPayload(JSONEncoder):
53
54
  if req.exception is not None:
54
55
  payload["exception"] = {
55
56
  "type": str(type(req.exception)),
56
- "message": str(req.exception),
57
+ "message": "".join(
58
+ traceback.format_exception(
59
+ type(req.exception), req.exception, req.exception.__traceback__
60
+ )
61
+ ),
57
62
  }
58
63
 
59
64
  return payload
@@ -119,6 +119,7 @@ async function get_request(request_id) {
119
119
 
120
120
  // the full stack is not present in request summary
121
121
  global.requests[request_id].initiator_id = data.initiator_id;
122
+ global.requests[request_id].exception = data.exception;
122
123
 
123
124
  global.requests[request_id].to_refresh = true;
124
125
 
@@ -363,8 +363,8 @@
363
363
  -- no exception --
364
364
  {{/exception}}
365
365
  {{#exception}}
366
- <span>{{exception.type}}</span>
367
- <pre>{{exception.message}}</pre>
366
+ <span class="header-cookie-value">{{exception.type}}</span>
367
+ <pre class="previewcode">{{exception.message}}</pre>
368
368
  {{/exception}}
369
369
  </script>
370
370
  </div>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: httpdbg
3
- Version: 0.29.0
3
+ Version: 0.29.2
4
4
  Summary: A very simple tool to debug HTTP(S) client requests
5
5
  Author-email: cle-b <cle@tictac.pm>
6
6
  License: Apache-2.0
@@ -36,7 +36,7 @@ def test_run_console_from_pyhttpdbg_entry_point_default(
36
36
  def test_run_console(httpbin, httpdbg_host, httpdbg_port):
37
37
  with httpdbg_srv(httpdbg_host, httpdbg_port) as records:
38
38
  with httprecord(records):
39
- new_console = run_console(test_mode=True)
39
+ new_console = run_console(records, test_mode=True)
40
40
  new_console.push("import requests")
41
41
  new_console.push(f"requests.get('{httpbin.url}/get')")
42
42
  with pytest.raises(SystemExit):
@@ -48,3 +48,31 @@ def test_run_console(httpbin, httpdbg_host, httpdbg_port):
48
48
 
49
49
  assert len(reqs) == 1
50
50
  assert reqs[list(reqs.keys())[0]]["url"] == httpbin.url + "/get"
51
+
52
+
53
+ @pytest.mark.group
54
+ def test_run_console_group(httpbin):
55
+
56
+ with httprecord() as records:
57
+ new_console = run_console(records, test_mode=True)
58
+ new_console.push("import requests")
59
+ new_console.push(f"requests.get('{httpbin.url}/get')")
60
+ new_console.push("if True:")
61
+ new_console.push(f" requests.post('{httpbin.url}/post')")
62
+ new_console.push("")
63
+ with pytest.raises(SystemExit):
64
+ new_console.push("exit()")
65
+
66
+ assert len(records) == 2
67
+ assert (
68
+ records.groups[records[0].group_id].label
69
+ == f"requests.get('{httpbin.url}/get')"
70
+ )
71
+ assert records.groups[records[0].group_id].full_label.endswith(
72
+ f">>> requests.get('{httpbin.url}/get') <===="
73
+ )
74
+
75
+ assert records.groups[records[1].group_id].label == "(block)"
76
+ assert records.groups[records[1].group_id].full_label.endswith(
77
+ f">>> if True:\n requests.post('{httpbin.url}/post')"
78
+ )
@@ -1,37 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import code
3
- from typing import Union
4
-
5
-
6
- def console_exit():
7
- raise SystemExit
8
-
9
-
10
- def run_console(test_mode: bool = False) -> Union[code.InteractiveConsole, None]:
11
- try:
12
- vars = globals()
13
- vars.update(locals())
14
-
15
- try:
16
- # adds history and autocompletion capabilities
17
- import readline
18
- import rlcompleter
19
-
20
- readline.set_completer(rlcompleter.Completer(vars).complete)
21
- readline.parse_and_bind("tab: complete")
22
- except ImportError: # pragma: no cover
23
- # readline is not available on Windows
24
- pass
25
-
26
- vars.update({"exit": console_exit})
27
-
28
- new_console = code.InteractiveConsole(vars)
29
- if not test_mode:
30
- new_console.interact() # pragma: no cover
31
- else:
32
- new_console.push("print('test_mode is on')")
33
- return new_console
34
- except SystemExit: # pragma: no cover
35
- pass
36
-
37
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes