hilda 2.0.11__tar.gz → 2.0.14__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 (77) hide show
  1. {hilda-2.0.11 → hilda-2.0.14}/.github/workflows/python-app.yml +1 -1
  2. {hilda-2.0.11 → hilda-2.0.14}/PKG-INFO +5 -3
  3. {hilda-2.0.11 → hilda-2.0.14}/README.md +2 -0
  4. {hilda-2.0.11 → hilda-2.0.14}/hilda/_version.py +2 -2
  5. {hilda-2.0.11 → hilda-2.0.14}/hilda/cli.py +6 -6
  6. {hilda-2.0.11 → hilda-2.0.14}/hilda/common.py +3 -3
  7. {hilda-2.0.11 → hilda-2.0.14}/hilda/hilda_client.py +47 -18
  8. {hilda-2.0.11 → hilda-2.0.14}/hilda/launch_lldb.py +3 -3
  9. {hilda-2.0.11 → hilda-2.0.14}/hilda/lldb_importer.py +1 -1
  10. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c_class.py +4 -5
  11. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c_symbol.py +6 -7
  12. {hilda-2.0.11 → hilda-2.0.14}/hilda/registers.py +1 -1
  13. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/all_image_infos.py +2 -2
  14. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/image_info.py +1 -1
  15. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/macho_load_commands.py +9 -11
  16. {hilda-2.0.11 → hilda-2.0.14}/hilda/symbol.py +4 -4
  17. {hilda-2.0.11 → hilda-2.0.14}/hilda/ui/views.py +5 -6
  18. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/PKG-INFO +5 -3
  19. {hilda-2.0.11 → hilda-2.0.14}/pyproject.toml +2 -2
  20. {hilda-2.0.11 → hilda-2.0.14}/.github/workflows/python-publish.yml +0 -0
  21. {hilda-2.0.11 → hilda-2.0.14}/.gitignore +0 -0
  22. {hilda-2.0.11 → hilda-2.0.14}/.pre-commit-config.yaml +0 -0
  23. {hilda-2.0.11 → hilda-2.0.14}/LICENSE +0 -0
  24. {hilda-2.0.11 → hilda-2.0.14}/gifs/.gitattributes +0 -0
  25. {hilda-2.0.11 → hilda-2.0.14}/gifs/ui.png +0 -0
  26. {hilda-2.0.11 → hilda-2.0.14}/gifs/xpc_print_message.gif +0 -0
  27. {hilda-2.0.11 → hilda-2.0.14}/hilda/__init__.py +0 -0
  28. {hilda-2.0.11 → hilda-2.0.14}/hilda/__main__.py +0 -0
  29. {hilda-2.0.11 → hilda-2.0.14}/hilda/exceptions.py +0 -0
  30. {hilda-2.0.11 → hilda-2.0.14}/hilda/hilda_ascii_art.html +0 -0
  31. {hilda-2.0.11 → hilda-2.0.14}/hilda/ipython_extensions/events.py +0 -0
  32. {hilda-2.0.11 → hilda-2.0.14}/hilda/ipython_extensions/keybindings.py +0 -0
  33. {hilda-2.0.11 → hilda-2.0.14}/hilda/ipython_extensions/magics.py +0 -0
  34. {hilda-2.0.11 → hilda-2.0.14}/hilda/lldb_entrypoint.py +0 -0
  35. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/from_ns_to_json.m +0 -0
  36. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/get_objectivec_class_by_module.m +0 -0
  37. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/get_objectivec_class_description.m +0 -0
  38. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/get_objectivec_symbol_data.m +0 -0
  39. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/lsof.m +0 -0
  40. {hilda-2.0.11 → hilda-2.0.14}/hilda/objective_c/to_ns_from_json.m +0 -0
  41. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/__init__.py +0 -0
  42. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/boringssl.py +0 -0
  43. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/collections.py +0 -0
  44. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/dyld.py +0 -0
  45. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/fs_utils.py +0 -0
  46. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/mach/CFRunLoopServiceMachPort_hooks.py +0 -0
  47. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/mach/__init__.py +0 -0
  48. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/__init__.py +0 -0
  49. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/apple_version.py +0 -0
  50. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/macho/macho.py +0 -0
  51. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/remotepairingd.py +0 -0
  52. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/syslog.py +0 -0
  53. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/uuid.py +0 -0
  54. {hilda-2.0.11 → hilda-2.0.14}/hilda/snippets/xpc.py +0 -0
  55. {hilda-2.0.11 → hilda-2.0.14}/hilda/symbols_jar.py +0 -0
  56. {hilda-2.0.11 → hilda-2.0.14}/hilda/ui/colors.json +0 -0
  57. {hilda-2.0.11 → hilda-2.0.14}/hilda/ui/ui_manager.py +0 -0
  58. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/SOURCES.txt +0 -0
  59. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/dependency_links.txt +0 -0
  60. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/entry_points.txt +0 -0
  61. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/requires.txt +0 -0
  62. {hilda-2.0.11 → hilda-2.0.14}/hilda.egg-info/top_level.txt +0 -0
  63. {hilda-2.0.11 → hilda-2.0.14}/requirements.txt +0 -0
  64. {hilda-2.0.11 → hilda-2.0.14}/setup.cfg +0 -0
  65. {hilda-2.0.11 → hilda-2.0.14}/tests/__init__.py +0 -0
  66. {hilda-2.0.11 → hilda-2.0.14}/tests/conftest.py +0 -0
  67. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_from_ns.py +0 -0
  68. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_hilda_client.py +0 -0
  69. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_monitor.py +0 -0
  70. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_ns.py +0 -0
  71. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_rebind_symbols.py +0 -0
  72. {hilda-2.0.11 → hilda-2.0.14}/tests/test_hilda_client/test_registers.py +0 -0
  73. {hilda-2.0.11 → hilda-2.0.14}/tests/test_snippets/test_xpc.py +0 -0
  74. {hilda-2.0.11 → hilda-2.0.14}/tests/test_symbols/test_objective_c_class.py +0 -0
  75. {hilda-2.0.11 → hilda-2.0.14}/tests/test_symbols/test_objective_c_symbol.py +0 -0
  76. {hilda-2.0.11 → hilda-2.0.14}/tests/test_symbols/test_symbol.py +0 -0
  77. {hilda-2.0.11 → hilda-2.0.14}/tests/test_symbols/test_symbols_jar.py +0 -0
@@ -16,7 +16,7 @@ jobs:
16
16
 
17
17
  strategy:
18
18
  matrix:
19
- python-version: [ 3.8, 3.9, "3.10", "3.11", "3.12" ]
19
+ python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13" ]
20
20
  os: [ macos-latest ]
21
21
 
22
22
  steps:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hilda
3
- Version: 2.0.11
3
+ Version: 2.0.14
4
4
  Summary: LLDB wrapped and empowered by iPython's features
5
5
  Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
6
6
  Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
@@ -31,13 +31,13 @@ Classifier: Operating System :: MacOS
31
31
  Classifier: Development Status :: 5 - Production/Stable
32
32
  Classifier: License :: OSI Approved :: MIT License
33
33
  Classifier: Programming Language :: Python :: 3
34
- Classifier: Programming Language :: Python :: 3.8
35
34
  Classifier: Programming Language :: Python :: 3.9
36
35
  Classifier: Programming Language :: Python :: 3.10
37
36
  Classifier: Programming Language :: Python :: 3.11
38
37
  Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
39
  Classifier: Programming Language :: Python :: 3 :: Only
40
- Requires-Python: >=3.8
40
+ Requires-Python: >=3.9
41
41
  Description-Content-Type: text/markdown
42
42
  License-File: LICENSE
43
43
  Requires-Dist: tqdm
@@ -189,6 +189,8 @@ Here is a gist of methods you can access from `p`:
189
189
  - Stop process.
190
190
  - `cont`
191
191
  - Continue process.
192
+ - `run_for`
193
+ - Run the process for given interval.
192
194
  - `detach`
193
195
  - Detach from process.
194
196
  Useful in order to exit gracefully so process doesn't get killed
@@ -132,6 +132,8 @@ Here is a gist of methods you can access from `p`:
132
132
  - Stop process.
133
133
  - `cont`
134
134
  - Continue process.
135
+ - `run_for`
136
+ - Run the process for given interval.
135
137
  - `detach`
136
138
  - Detach from process.
137
139
  Useful in order to exit gracefully so process doesn't get killed
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '2.0.11'
16
- __version_tuple__ = version_tuple = (2, 0, 11)
15
+ __version__ = version = '2.0.14'
16
+ __version_tuple__ = version_tuple = (2, 0, 14)
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from pathlib import Path
3
- from typing import List, Optional
3
+ from typing import Optional
4
4
 
5
5
  import click
6
6
  import coloredlogs
@@ -23,7 +23,7 @@ def cli():
23
23
  startup_files_option = click.option('-f', '--startup_files', multiple=True, help='Files to run on start')
24
24
 
25
25
 
26
- def parse_envp(ctx: click.Context, param: click.Parameter, value: List[str]) -> List[str]:
26
+ def parse_envp(ctx: click.Context, param: click.Parameter, value: list[str]) -> list[str]:
27
27
  env_list = []
28
28
  for item in value:
29
29
  try:
@@ -38,7 +38,7 @@ def parse_envp(ctx: click.Context, param: click.Parameter, value: List[str]) ->
38
38
  @click.argument('hostname', default='localhost')
39
39
  @click.argument('port', type=click.INT, default=DEFAULT_HILDA_PORT)
40
40
  @startup_files_option
41
- def remote(hostname: str, port: int, startup_files: List[str]) -> None:
41
+ def remote(hostname: str, port: int, startup_files: list[str]) -> None:
42
42
  """ Connect to remote debugserver at given address """
43
43
  with create_hilda_client_using_remote_attach(hostname, port) as hilda_client:
44
44
  hilda_client.interact(startup_files=startup_files)
@@ -48,7 +48,7 @@ def remote(hostname: str, port: int, startup_files: List[str]) -> None:
48
48
  @click.option('-n', '--name', help='process name to attach')
49
49
  @click.option('-p', '--pid', type=click.INT, help='pid to attach')
50
50
  @startup_files_option
51
- def attach(name: Optional[str], pid: Optional[int], startup_files: List[str]) -> None:
51
+ def attach(name: Optional[str], pid: Optional[int], startup_files: list[str]) -> None:
52
52
  """ Attach to given process and start a lldb shell """
53
53
  if name is not None:
54
54
  hilda_client = create_hilda_client_using_attach_by_name(name)
@@ -78,9 +78,9 @@ def cli_bare() -> None:
78
78
  @click.option('--cwd', help='Set the working directory for the process')
79
79
  @click.option('--flags', type=click.INT, default=0, help='Launch flags (bitmask)')
80
80
  @startup_files_option
81
- def launch(exec_path: str, argv: List[str], envp: List[str], stdin: Optional[Path],
81
+ def launch(exec_path: str, argv: list[str], envp: list[str], stdin: Optional[Path],
82
82
  stdout: Optional[Path], stderr: Optional[Path], cwd: Optional[Path], flags: Optional[int],
83
- startup_files: List[str]) -> None:
83
+ startup_files: list[str]) -> None:
84
84
  """ Attach to a given process and start a lldb shell """
85
85
  argv = list(argv)
86
86
  envp = list(envp)
@@ -1,14 +1,14 @@
1
1
  from datetime import datetime
2
- from typing import Any, List, Mapping, Tuple, Union
2
+ from typing import Any, Union
3
3
 
4
4
  import inquirer3
5
5
  from inquirer3.themes import GreenPassion
6
6
 
7
7
  CfSerializable = Union[
8
- Mapping[str, Any], List, Tuple[Any, ...], str, bool, float, bytes, datetime, None]
8
+ dict[str, Any], list, tuple[Any, ...], str, bool, float, bytes, datetime, None]
9
9
 
10
10
 
11
- def selection_prompt(options_list: List):
11
+ def selection_prompt(options_list: list):
12
12
  question = [inquirer3.List('choice', message='choose device', choices=options_list, carousel=True)]
13
13
  result = inquirer3.prompt(question, theme=GreenPassion(), raise_keyboard_interrupt=True)
14
14
  return result['choice']
@@ -16,7 +16,7 @@ from dataclasses import dataclass, field
16
16
  from datetime import datetime, timezone
17
17
  from functools import cached_property, wraps
18
18
  from pathlib import Path
19
- from typing import Any, Callable, List, Optional, Union
19
+ from typing import Any, Callable, Optional, Union
20
20
 
21
21
  import hexdump
22
22
  import IPython
@@ -125,9 +125,29 @@ def stop_is_needed(func: Callable):
125
125
  return wrapper
126
126
 
127
127
 
128
- class HildaClient:
129
- Breakpoint = namedtuple('Breakpoint', 'address options forced callback')
128
+ class HildaBreakpoint:
129
+ def __init__(self, hilda_client: 'HildaClient', lldb_breakpoint: lldb.SBBreakpoint,
130
+ address: Union[str, int], forced: bool = False, options: Optional[typing.Mapping] = None,
131
+ callback: Optional[Callable] = None) -> None:
132
+ self._hilda_client = hilda_client
133
+ self.address = address
134
+ self.forced = forced
135
+ self.options = options
136
+ self.callback = callback
137
+ self.lldb_breakpoint = lldb_breakpoint
138
+
139
+ def __repr__(self) -> str:
140
+ return (f'<{self.__class__.__name__} LLDB:{self.lldb_breakpoint} FORCED:{self.forced} OPTIONS:{self.options} '
141
+ f'CALLBACK:{self.callback}>')
142
+
143
+ def __str__(self) -> str:
144
+ return repr(self)
130
145
 
146
+ def remove(self) -> None:
147
+ self._hilda_client.remove_hilda_breakpoint(self.lldb_breakpoint.id)
148
+
149
+
150
+ class HildaClient:
131
151
  RETVAL_BIT_COUNT = 64
132
152
 
133
153
  def __init__(self, debugger: lldb.SBDebugger):
@@ -146,6 +166,7 @@ class HildaClient:
146
166
  self._dynamic_env_loaded = False
147
167
  self._symbols_loaded = False
148
168
  self.globals: typing.MutableMapping[str, Any] = globals()
169
+ self._hilda_root = Path(__file__).parent
149
170
 
150
171
  # the frame called within the context of the hit BP
151
172
  self._bp_frame = None
@@ -168,12 +189,12 @@ class HildaClient:
168
189
  Get dictionary of all open FDs
169
190
  :return: Mapping between open FDs and their paths
170
191
  """
171
- data = (Path(__file__).parent / 'objective_c' / 'lsof.m').read_text()
192
+ data = (self._hilda_root / 'objective_c' / 'lsof.m').read_text()
172
193
  result = json.loads(self.po(data))
173
194
  # convert FDs into int
174
195
  return {int(k): v for k, v in result.items()}
175
196
 
176
- def bt(self, should_print: bool = False, depth: Optional[int] = None) -> List[Union[str, lldb.SBFrame]]:
197
+ def bt(self, should_print: bool = False, depth: Optional[int] = None) -> list[Union[str, lldb.SBFrame]]:
177
198
  """ Print an improved backtrace. """
178
199
  backtrace = []
179
200
  for i, frame in enumerate(self.thread.frames):
@@ -347,6 +368,16 @@ class HildaClient:
347
368
  if not self.process.Stop().Success():
348
369
  self.log_critical('failed to stop process')
349
370
 
371
+ def run_for(self, seconds: float) -> None:
372
+ """
373
+ Run the process for a given time
374
+ :return:
375
+ """
376
+ self.cont()
377
+ self.logger.info(f'Running for {seconds} seconds')
378
+ time.sleep(seconds)
379
+ self.stop()
380
+
350
381
  def cont(self, *args) -> None:
351
382
  """ Continue process. """
352
383
  is_running = self.process.GetState() == lldb.eStateRunning
@@ -462,7 +493,7 @@ class HildaClient:
462
493
  with self.stopped():
463
494
  return self.evaluate_expression(call_expression)
464
495
 
465
- def monitor(self, address, condition: str = None, **options) -> lldb.SBBreakpoint:
496
+ def monitor(self, address, condition: str = None, **options) -> HildaBreakpoint:
466
497
  """
467
498
  Monitor every time a given address is called
468
499
 
@@ -602,7 +633,7 @@ class HildaClient:
602
633
  if remove_forced or not bp.forced:
603
634
  self.remove_hilda_breakpoint(bp_id)
604
635
 
605
- def remove_hilda_breakpoint(self, bp_id):
636
+ def remove_hilda_breakpoint(self, bp_id: int) -> None:
606
637
  """
607
638
  Remove a single breakpoint placed by Hilda
608
639
  :param bp_id: Breakpoint's ID
@@ -636,7 +667,7 @@ class HildaClient:
636
667
  print(highlight(entitlements, XmlLexer(), TerminalTrueColorFormatter()))
637
668
 
638
669
  def bp(self, address_or_name: Union[int, str], callback: Optional[Callable] = None, condition: str = None,
639
- forced=False, module_name: Optional[str] = None, **options) -> lldb.SBBreakpoint:
670
+ forced=False, module_name: Optional[str] = None, **options) -> HildaBreakpoint:
640
671
  """
641
672
  Add a breakpoint
642
673
  :param address_or_name:
@@ -665,15 +696,14 @@ class HildaClient:
665
696
  bp.SetCondition(condition)
666
697
 
667
698
  # add into Hilda's internal list of breakpoints
668
- self.breakpoints[bp.id] = HildaClient.Breakpoint(
669
- address=address_or_name, options=options, forced=forced, callback=callback
670
- )
699
+ self.breakpoints[bp.id] = HildaBreakpoint(self, bp, address=address_or_name, forced=forced, options=options,
700
+ callback=callback)
671
701
 
672
702
  if callback is not None:
673
703
  bp.SetScriptCallbackFunction('lldb.hilda_client.bp_callback_router')
674
704
 
675
705
  self.log_info(f'Breakpoint #{bp.id} has been set')
676
- return bp
706
+ return self.breakpoints[bp.id]
677
707
 
678
708
  def bp_callback_router(self, frame, bp_loc, *_):
679
709
  """
@@ -828,8 +858,7 @@ class HildaClient:
828
858
  json_data = json.dumps({'root': data}, default=self._to_ns_json_default)
829
859
  except TypeError as e:
830
860
  raise ConvertingToNsObjectError from e
831
-
832
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'to_ns_from_json.m').read_text()
861
+ obj_c_code = (self._hilda_root / 'objective_c' / 'to_ns_from_json.m').read_text()
833
862
  expression = obj_c_code.replace('__json_object_dump__', json_data.replace('"', r'\"'))
834
863
  try:
835
864
  return self.evaluate_expression(expression)
@@ -842,7 +871,7 @@ class HildaClient:
842
871
  :param address: NS object.
843
872
  :return: Python object.
844
873
  """
845
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'from_ns_to_json.m').read_text()
874
+ obj_c_code = (self._hilda_root / 'objective_c' / 'from_ns_to_json.m').read_text()
846
875
  address = f'0x{address:x}' if isinstance(address, int) else address
847
876
  expression = obj_c_code.replace('__ns_object_address__', address)
848
877
  try:
@@ -958,7 +987,7 @@ class HildaClient:
958
987
  """
959
988
  block = self.symbols.malloc(size)
960
989
  if block == 0:
961
- raise IOError(f'failed to allocate memory of size: {size} bytes')
990
+ raise OSError(f'failed to allocate memory of size: {size} bytes')
962
991
 
963
992
  try:
964
993
  yield block
@@ -1066,7 +1095,7 @@ class HildaClient:
1066
1095
  self.cont()
1067
1096
 
1068
1097
  def interact(self, additional_namespace: Optional[typing.Mapping] = None,
1069
- startup_files: Optional[List[str]] = None) -> None:
1098
+ startup_files: Optional[list[str]] = None) -> None:
1070
1099
  """ Start an interactive Hilda shell """
1071
1100
  if not self._dynamic_env_loaded:
1072
1101
  self.init_dynamic_environment()
@@ -1212,7 +1241,7 @@ class HildaClient:
1212
1241
  continue
1213
1242
  objc_classlist = m.FindSection('__DATA').FindSubSection('__objc_classlist')
1214
1243
  objc_classlist_addr = self.symbol(objc_classlist.GetLoadAddress(self.target))
1215
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'get_objectivec_class_by_module.m').read_text()
1244
+ obj_c_code = (self._hilda_root / 'objective_c' / 'get_objectivec_class_by_module.m').read_text()
1216
1245
  obj_c_code = obj_c_code.replace('__count_objc_class', f'{objc_classlist.size // 8}').replace(
1217
1246
  '__objc_class_list',
1218
1247
  f'{objc_classlist_addr}')
@@ -3,7 +3,7 @@ import os
3
3
  import sys
4
4
  from abc import ABC, abstractmethod
5
5
  from threading import Thread
6
- from typing import List, Optional
6
+ from typing import Optional
7
7
 
8
8
  from hilda.exceptions import LLDBError
9
9
  from hilda.hilda_client import HildaClient
@@ -146,7 +146,7 @@ class LLDBAttachName(LLDBListenerThread):
146
146
 
147
147
 
148
148
  class LLDBLaunch(LLDBListenerThread):
149
- def __init__(self, exec_path: str, argv: Optional[List[str]] = None, envp: Optional[List[str]] = None,
149
+ def __init__(self, exec_path: str, argv: Optional[list[str]] = None, envp: Optional[list[str]] = None,
150
150
  stdin: Optional[str] = None,
151
151
  stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
152
152
  flags: Optional[int] = 0):
@@ -189,7 +189,7 @@ def create_hilda_client_using_remote_attach(
189
189
 
190
190
 
191
191
  def create_hilda_client_using_launch(
192
- exec_path: str, argv: Optional[List] = None, envp: Optional[List] = None, stdin: Optional[str] = None,
192
+ exec_path: str, argv: Optional[list] = None, envp: Optional[list] = None, stdin: Optional[str] = None,
193
193
  stdout: Optional[str] = None, stderr: Optional[str] = None, wd: Optional[str] = None,
194
194
  flags: Optional[int] = 0) -> HildaClient:
195
195
  lldb_t = LLDBLaunch(exec_path, argv, envp, stdin, stdout, stderr, wd, flags)
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
8
8
 
9
9
 
10
10
  def get_lldb_python_path() -> str:
11
- result = subprocess.run(['lldb', '-P'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True)
11
+ result = subprocess.run(['lldb', '-P'], capture_output=True, text=True, check=True)
12
12
  return result.stdout.strip()
13
13
 
14
14
 
@@ -3,7 +3,6 @@ import time
3
3
  from collections import namedtuple
4
4
  from dataclasses import dataclass, field
5
5
  from functools import partial
6
- from pathlib import Path
7
6
  from typing import Any
8
7
  from uuid import uuid4
9
8
 
@@ -90,7 +89,7 @@ class Method:
90
89
  return f'{prefix} {name}; // 0x{self.address:x} (returns: {self.return_type})\n'
91
90
 
92
91
 
93
- class Class(object):
92
+ class Class:
94
93
  """
95
94
  Wrapper for ObjectiveC Class object.
96
95
  """
@@ -120,7 +119,7 @@ class Class(object):
120
119
  :param hilda.hilda_client.HildaClient client: Hilda client.
121
120
  :param class_name: Class name.
122
121
  """
123
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'get_objectivec_class_description.m').read_text()
122
+ obj_c_code = (client._hilda_root / 'objective_c' / 'get_objectivec_class_description.m').read_text()
124
123
  obj_c_code = obj_c_code.replace('__class_address__', '0').replace('__class_name__', class_name)
125
124
  class_symbol = Class(client, class_data=json.loads(client.po(obj_c_code)))
126
125
  if class_symbol.name != class_name:
@@ -143,7 +142,7 @@ class Class(object):
143
142
  Reload class object data.
144
143
  Should be used whenever the class layout changes (for example, during method swizzling)
145
144
  """
146
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'get_objectivec_class_description.m').read_text()
145
+ obj_c_code = (self._client._hilda_root / 'objective_c' / 'get_objectivec_class_description.m').read_text()
147
146
  obj_c_code = obj_c_code.replace('__class_address__', f'{self._class_object:d}')
148
147
  obj_c_code = obj_c_code.replace('__class_name__', self.name)
149
148
  self._load_class_data(json.loads(self._client.po(obj_c_code)))
@@ -274,7 +273,7 @@ class Class(object):
274
273
  if method.is_class:
275
274
  result.add(method.name.replace(':', '_'))
276
275
 
277
- result.update(list(super(Class, self).__dir__()))
276
+ result.update(list(super().__dir__()))
278
277
  return list(result)
279
278
 
280
279
  def __str__(self):
@@ -2,7 +2,6 @@ import json
2
2
  from contextlib import suppress
3
3
  from dataclasses import dataclass
4
4
  from functools import partial
5
- from pathlib import Path
6
5
 
7
6
  from objc_types_decoder.decode import decode as decode_type
8
7
  from pygments import highlight
@@ -43,7 +42,7 @@ class ObjectiveCSymbol(Symbol):
43
42
  :return: ObjectiveCSymbol object.
44
43
  :rtype: ObjectiveCSymbol
45
44
  """
46
- symbol = super(ObjectiveCSymbol, cls).create(value, client)
45
+ symbol = super().create(value, client)
47
46
  symbol.ivars = [] # type: List[Ivar]
48
47
  symbol.properties = [] # type: List[Property]
49
48
  symbol.methods = [] # type: List[Method]
@@ -60,7 +59,7 @@ class ObjectiveCSymbol(Symbol):
60
59
  self.methods.clear()
61
60
  self.class_ = None
62
61
 
63
- obj_c_code = (Path(__file__).parent / 'objective_c' / 'get_objectivec_symbol_data.m').read_text()
62
+ obj_c_code = (self._client._hilda_root / 'objective_c' / 'get_objectivec_symbol_data.m').read_text()
64
63
  obj_c_code = obj_c_code.replace('__symbol_address__', f'{self:d}')
65
64
  data = json.loads(self._client.po(obj_c_code))
66
65
 
@@ -184,12 +183,12 @@ class ObjectiveCSymbol(Symbol):
184
183
  for method in sup.methods:
185
184
  result.add(method.name.replace(':', '_'))
186
185
 
187
- result.update(list(super(ObjectiveCSymbol, self).__dir__()))
186
+ result.update(list(super().__dir__()))
188
187
  return list(result)
189
188
 
190
189
  def __getitem__(self, item):
191
190
  if isinstance(item, int):
192
- return super(ObjectiveCSymbol, self).__getitem__(item)
191
+ return super().__getitem__(item)
193
192
 
194
193
  # Ivars
195
194
  for ivar in self.ivars:
@@ -218,7 +217,7 @@ class ObjectiveCSymbol(Symbol):
218
217
 
219
218
  def __setitem__(self, key, value):
220
219
  if isinstance(key, int):
221
- super(ObjectiveCSymbol, self).__setitem__(key, value)
220
+ super().__setitem__(key, value)
222
221
  return
223
222
 
224
223
  with suppress(SettingIvarError):
@@ -233,7 +232,7 @@ class ObjectiveCSymbol(Symbol):
233
232
  try:
234
233
  self._set_ivar(key, value)
235
234
  except SettingIvarError:
236
- super(ObjectiveCSymbol, self).__setattr__(key, value)
235
+ super().__setattr__(key, value)
237
236
 
238
237
  def __str__(self):
239
238
  return self._to_str(False)
@@ -1,4 +1,4 @@
1
- class Registers(object):
1
+ class Registers:
2
2
  """
3
3
  Wrapper for more convenient access to modify current frame's registers
4
4
  """
@@ -47,7 +47,7 @@ all_image_infos_t = Struct(
47
47
  )
48
48
 
49
49
 
50
- class AllImageInfos(object):
50
+ class AllImageInfos:
51
51
  def reload(self):
52
52
  with lldb.hilda_client.stopped(1):
53
53
  all_image_infos_symbol = lldb.hilda_client.symbol(lldb.hilda_client.symbols.dyld_all_image_infos)
@@ -98,7 +98,7 @@ class AllImageInfos(object):
98
98
  if len(unique_images) > 1:
99
99
  image = AllImageInfos.__select_specific_image(unique_images)
100
100
 
101
- dependencies = set([image_name.path for image_name in image.load_commands.dylib_commands])
101
+ dependencies = {image_name.path for image_name in image.load_commands.dylib_commands}
102
102
 
103
103
  return dependencies
104
104
 
@@ -22,7 +22,7 @@ dyld_uuid_info_t = Struct(
22
22
  )
23
23
 
24
24
 
25
- class ImageInfo(object):
25
+ class ImageInfo:
26
26
  def __init__(self, image_info_data):
27
27
  self.__image_info_data = image_info_data
28
28
  self.__file_path = image_info_data.imageFilePath
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
1
  from construct import Array, Bytes, Enum, Hex, Int8ul, Int32ul, Int64ul, PaddedString, Pointer, Seek, Struct, Switch, \
4
2
  Tell, this
5
3
 
@@ -142,7 +140,7 @@ load_command_t = Struct(
142
140
  )
143
141
 
144
142
 
145
- class LoadCommand(object):
143
+ class LoadCommand:
146
144
  def __init__(self, load_command_data):
147
145
  self.__load_command_data = load_command_data
148
146
  self.__cmd = load_command_data.cmd
@@ -160,7 +158,7 @@ class LoadCommand(object):
160
158
 
161
159
  class DylibCommand(LoadCommand):
162
160
  def __init__(self, load_command_data):
163
- super(DylibCommand, self).__init__(load_command_data)
161
+ super().__init__(load_command_data)
164
162
  dylib_data = load_command_data.data.dylib
165
163
 
166
164
  self.__path = dylib_data.lc_str.name
@@ -181,7 +179,7 @@ class DylibCommand(LoadCommand):
181
179
 
182
180
  class Segment64Command(LoadCommand):
183
181
  def __init__(self, load_command_data):
184
- super(Segment64Command, self).__init__(load_command_data)
182
+ super().__init__(load_command_data)
185
183
 
186
184
  self.__segname = load_command_data.data.segname
187
185
  self.__vmaddr = load_command_data.data.vmaddr
@@ -210,7 +208,7 @@ class Segment64Command(LoadCommand):
210
208
 
211
209
  class UUIDCommand(LoadCommand):
212
210
  def __init__(self, load_command_data):
213
- super(UUIDCommand, self).__init__(load_command_data)
211
+ super().__init__(load_command_data)
214
212
 
215
213
  self.__uuid = load_command_data.data.uuid
216
214
 
@@ -223,7 +221,7 @@ class UUIDCommand(LoadCommand):
223
221
 
224
222
  class BuildVersionCommand(LoadCommand):
225
223
  def __init__(self, load_command_data):
226
- super(BuildVersionCommand, self).__init__(load_command_data)
224
+ super().__init__(load_command_data)
227
225
 
228
226
  self.__platform = load_command_data.data.platform
229
227
  self.__minos = load_command_data.data.platform
@@ -252,7 +250,7 @@ class BuildVersionCommand(LoadCommand):
252
250
 
253
251
  class UnimplementedCommand(LoadCommand):
254
252
  def __init__(self, load_command_data):
255
- super(UnimplementedCommand, self).__init__(load_command_data)
253
+ super().__init__(load_command_data)
256
254
 
257
255
  self.__bytes = load_command_data.data
258
256
 
@@ -287,15 +285,15 @@ class LoadCommands:
287
285
  return self.__load_commands
288
286
 
289
287
  @property
290
- def segment_commands(self) -> List[Segment64Command]:
288
+ def segment_commands(self) -> list[Segment64Command]:
291
289
  return [segment_command for segment_command in self.__load_commands if
292
290
  isinstance(segment_command, Segment64Command)]
293
291
 
294
292
  @property
295
- def dylib_commands(self) -> List[DylibCommand]:
293
+ def dylib_commands(self) -> list[DylibCommand]:
296
294
  return [dylib_command for dylib_command in self.__load_commands if isinstance(dylib_command, DylibCommand)]
297
295
 
298
- def find(self, predicate=None) -> List[LoadCommand]:
296
+ def find(self, predicate=None) -> list[LoadCommand]:
299
297
  if predicate is None:
300
298
  return self.__load_commands
301
299
 
@@ -18,7 +18,7 @@ class SymbolFormatField(FormatField):
18
18
  """
19
19
 
20
20
  def __init__(self, client):
21
- super(SymbolFormatField, self).__init__('<', 'Q')
21
+ super().__init__('<', 'Q')
22
22
  self._client = client
23
23
 
24
24
  def _parse(self, stream, context, path):
@@ -139,10 +139,10 @@ class Symbol(int):
139
139
  def peek_str(self) -> str:
140
140
  return self._client.peek_str(self)
141
141
 
142
- def monitor(self, **args) -> lldb.SBBreakpoint:
142
+ def monitor(self, **args):
143
143
  return self._client.monitor(self, **args)
144
144
 
145
- def bp(self, callback=None, **args) -> lldb.SBBreakpoint:
145
+ def bp(self, callback=None, **args):
146
146
  return self._client.bp(self, callback, **args)
147
147
 
148
148
  def disass(self, size, **args) -> lldb.SBInstructionList:
@@ -165,7 +165,7 @@ class Symbol(int):
165
165
  elif whence == os.SEEK_SET:
166
166
  self._offset = offset - self
167
167
  else:
168
- raise IOError('Unsupported whence')
168
+ raise OSError('Unsupported whence')
169
169
 
170
170
  def read(self, count: int) -> bytes:
171
171
  """ Construct compliance. """
@@ -1,6 +1,5 @@
1
1
  import shutil
2
2
  from abc import abstractmethod
3
- from typing import List, Mapping
4
3
 
5
4
  from click import style
6
5
  from lldb import SBAddress
@@ -9,7 +8,7 @@ from tabulate import tabulate
9
8
  WORD_SIZE = 8
10
9
 
11
10
 
12
- def dict_diff(d1: Mapping, d2: Mapping) -> List[str]:
11
+ def dict_diff(d1: dict, d2: dict) -> list[str]:
13
12
  """ Returns a list of keys whose values have changed """
14
13
  changed_keys = []
15
14
  if d1 is None or d2 is None:
@@ -59,7 +58,7 @@ class StackView(View):
59
58
  """
60
59
  super().__init__(hilda_client, 'Stack', color_scheme)
61
60
  self.depth = depth
62
- self.prev: Mapping[int, str] = None
61
+ self.prev: dict[int, str] = None
63
62
 
64
63
  def __str__(self) -> str:
65
64
  """ Format stack view for printing """
@@ -80,7 +79,7 @@ class StackView(View):
80
79
 
81
80
  return super().__str__() + '\n'.join(fmt_parts)
82
81
 
83
- def _create_mapping(self) -> Mapping[int, str]:
82
+ def _create_mapping(self) -> dict[int, str]:
84
83
  """ Generate mapping of stack address:data"""
85
84
  base_addr = self.hilda.frame.sp
86
85
  stack_mapping = {}
@@ -103,7 +102,7 @@ class RegistersView(View):
103
102
  `prev` saves the last registers stats inorder to perform diff check.
104
103
  """
105
104
  super().__init__(hilda_client, 'Registers', color_scheme)
106
- self.prev: Mapping[str, str] = None
105
+ self.prev: dict[str, str] = None
107
106
  self.rtype = rtype
108
107
 
109
108
  def __str__(self) -> str:
@@ -133,7 +132,7 @@ class RegistersView(View):
133
132
 
134
133
  return super().__str__() + tabulate(list(zip(*list_of_lists)), tablefmt='plain', numalign='left')
135
134
 
136
- def _create_mapping(self) -> Mapping[str, str]:
135
+ def _create_mapping(self) -> dict[str, str]:
137
136
  """ Generate mapping of registers name:data"""
138
137
  regs = self._get_registers()
139
138
  regs_mapping = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hilda
3
- Version: 2.0.11
3
+ Version: 2.0.14
4
4
  Summary: LLDB wrapped and empowered by iPython's features
5
5
  Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
6
6
  Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>, netanel cohen <netanelc305@protonmail.com>
@@ -31,13 +31,13 @@ Classifier: Operating System :: MacOS
31
31
  Classifier: Development Status :: 5 - Production/Stable
32
32
  Classifier: License :: OSI Approved :: MIT License
33
33
  Classifier: Programming Language :: Python :: 3
34
- Classifier: Programming Language :: Python :: 3.8
35
34
  Classifier: Programming Language :: Python :: 3.9
36
35
  Classifier: Programming Language :: Python :: 3.10
37
36
  Classifier: Programming Language :: Python :: 3.11
38
37
  Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
39
  Classifier: Programming Language :: Python :: 3 :: Only
40
- Requires-Python: >=3.8
40
+ Requires-Python: >=3.9
41
41
  Description-Content-Type: text/markdown
42
42
  License-File: LICENSE
43
43
  Requires-Dist: tqdm
@@ -189,6 +189,8 @@ Here is a gist of methods you can access from `p`:
189
189
  - Stop process.
190
190
  - `cont`
191
191
  - Continue process.
192
+ - `run_for`
193
+ - Run the process for given interval.
192
194
  - `detach`
193
195
  - Detach from process.
194
196
  Useful in order to exit gracefully so process doesn't get killed
@@ -2,7 +2,7 @@
2
2
  name = "hilda"
3
3
  description = "LLDB wrapped and empowered by iPython's features"
4
4
  readme = "README.md"
5
- requires-python = ">=3.8"
5
+ requires-python = ">=3.9"
6
6
  license = { file = "LICENSE" }
7
7
  keywords = ["python", "debugger", "lldb", "ipython", "ios", "debug"]
8
8
  authors = [
@@ -20,11 +20,11 @@ classifiers = [
20
20
  "Development Status :: 5 - Production/Stable",
21
21
  "License :: OSI Approved :: MIT License",
22
22
  "Programming Language :: Python :: 3",
23
- "Programming Language :: Python :: 3.8",
24
23
  "Programming Language :: Python :: 3.9",
25
24
  "Programming Language :: Python :: 3.10",
26
25
  "Programming Language :: Python :: 3.11",
27
26
  "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
28
  "Programming Language :: Python :: 3 :: Only",
29
29
  ]
30
30
  dynamic = ["dependencies", "version"]
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
File without changes
File without changes
File without changes
File without changes