PyMemoryEditor 3.3.3__tar.gz → 3.4.1__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 (212) hide show
  1. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PKG-INFO +7 -4
  2. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/__init__.py +1 -1
  3. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/linux/process.py +3 -0
  4. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/macos/process.py +4 -0
  5. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/toolset.py +4 -0
  6. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/abstract.py +38 -8
  7. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/process.py +4 -0
  8. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/conf.py +1 -1
  9. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/pyproject.toml +11 -1
  10. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_parsing.py +20 -0
  11. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_toolset.py +2 -2
  12. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_write_safety.py +62 -0
  13. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/.flake8 +0 -0
  14. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/.gitignore +0 -0
  15. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/.readthedocs.yaml +0 -0
  16. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/CODE_OF_CONDUCT.md +0 -0
  17. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/CONTRIBUTING.md +0 -0
  18. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/LICENSE +0 -0
  19. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/Makefile +0 -0
  20. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/__main__.py +0 -0
  21. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/__init__.py +0 -0
  22. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/_auto_refresh_dialog.py +0 -0
  23. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/_gc.py +0 -0
  24. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/_icon.py +0 -0
  25. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/_widgets.py +0 -0
  26. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/application.py +0 -0
  27. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/assets/icon.svg +0 -0
  28. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/cheat_entry.py +0 -0
  29. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/cheat_poll_worker.py +0 -0
  30. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/cheat_table.py +0 -0
  31. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/log_console_dialog.py +0 -0
  32. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/main_window.py +0 -0
  33. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/memory_map_dialog.py +0 -0
  34. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/memory_viewer_dialog.py +0 -0
  35. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/modules_dialog.py +0 -0
  36. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/open_process_dialog.py +0 -0
  37. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/pointer_chain_dialog.py +0 -0
  38. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/pointer_scan_dialog.py +0 -0
  39. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/results_view.py +0 -0
  40. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/scan_types.py +0 -0
  41. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/scan_worker.py +0 -0
  42. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/scanner_panel.py +0 -0
  43. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/threads_dialog.py +0 -0
  44. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/app/value_types.py +0 -0
  45. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/enums.py +0 -0
  46. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/linux/functions.py +0 -0
  47. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/linux/libc.py +0 -0
  48. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/linux/types.py +0 -0
  49. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/macos/__init__.py +0 -0
  50. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/macos/functions.py +0 -0
  51. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/macos/libsystem.py +0 -0
  52. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/macos/types.py +0 -0
  53. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/__init__.py +0 -0
  54. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/__main__.py +0 -0
  55. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/config.py +0 -0
  56. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/policy.py +0 -0
  57. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/procinfo.py +0 -0
  58. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/server.py +0 -0
  59. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/mcp/session.py +0 -0
  60. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/__init__.py +0 -0
  61. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/_guard.py +0 -0
  62. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/errors.py +0 -0
  63. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/info.py +0 -0
  64. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/module_info.py +0 -0
  65. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/pointer_scan.py +0 -0
  66. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/region.py +0 -0
  67. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/remote_pointer.py +0 -0
  68. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/scanning.py +0 -0
  69. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/thread_info.py +0 -0
  70. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/process/util.py +0 -0
  71. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/py.typed +0 -0
  72. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/util/__init__.py +0 -0
  73. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/util/convert.py +0 -0
  74. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/util/pattern.py +0 -0
  75. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/util/scan.py +0 -0
  76. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/util/scan_numpy.py +0 -0
  77. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/__init__.py +0 -0
  78. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/memory_allocation_states.py +0 -0
  79. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/memory_protections.py +0 -0
  80. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/memory_types.py +0 -0
  81. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/process_operations.py +0 -0
  82. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/enums/standard_access_rights.py +0 -0
  83. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/functions.py +0 -0
  84. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/PyMemoryEditor/win32/types.py +0 -0
  85. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/README.md +0 -0
  86. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/SECURITY.md +0 -0
  87. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/assets/screenshots/app.png +0 -0
  88. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/assets/social-preview.html +0 -0
  89. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/assets/social-preview.png +0 -0
  90. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/_static/.gitkeep +0 -0
  91. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/_static/custom.css +0 -0
  92. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/_templates/layout.html +0 -0
  93. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/enums.md +0 -0
  94. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/errors.md +0 -0
  95. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/index.md +0 -0
  96. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/memory-region.md +0 -0
  97. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/module-info.md +0 -0
  98. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/openprocess.md +0 -0
  99. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/pointer-path.md +0 -0
  100. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/remote-pointer.md +0 -0
  101. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/thread-info.md +0 -0
  102. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/api/utilities.md +0 -0
  103. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/app.md +0 -0
  104. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/contributing.md +0 -0
  105. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/funding.md +0 -0
  106. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/glossary.md +0 -0
  107. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/allocate-free.md +0 -0
  108. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/logging.md +0 -0
  109. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/memory-regions.md +0 -0
  110. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/modules-threads.md +0 -0
  111. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/opening-process.md +0 -0
  112. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/pattern-scan.md +0 -0
  113. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/pointer-scan.md +0 -0
  114. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/pointers.md +0 -0
  115. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/read-write.md +0 -0
  116. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/guide/searching.md +0 -0
  117. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/index.md +0 -0
  118. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/installation.md +0 -0
  119. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/license.md +0 -0
  120. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/mcp.md +0 -0
  121. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/platform-notes.md +0 -0
  122. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/quickstart.md +0 -0
  123. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/requirements.txt +0 -0
  124. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/troubleshooting.md +0 -0
  125. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/docs/why.md +0 -0
  126. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/__init__.py +0 -0
  127. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/conftest.py +0 -0
  128. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_cheat_entry.py +0 -0
  129. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_gc.py +0 -0
  130. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_pointer_parsing.py +0 -0
  131. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_scan_request.py +0 -0
  132. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_smoke.py +0 -0
  133. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_value_types.py +0 -0
  134. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_app_widgets.py +0 -0
  135. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_auto_refresh_dialog.py +0 -0
  136. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_cheat_poll_worker.py +0 -0
  137. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_main_window_lifecycle.py +0 -0
  138. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_open_process_dialog.py +0 -0
  139. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_refine_scan_worker.py +0 -0
  140. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/app/test_results_model.py +0 -0
  141. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/conftest.py +0 -0
  142. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/core/__init__.py +0 -0
  143. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/core/test_abstract_process.py +0 -0
  144. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/core/test_errors.py +0 -0
  145. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/core/test_logger.py +0 -0
  146. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/__init__.py +0 -0
  147. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/conftest.py +0 -0
  148. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_argument_fuzz.py +0 -0
  149. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_fake_fidelity.py +0 -0
  150. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_live.py +0 -0
  151. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_policy.py +0 -0
  152. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_registry.py +0 -0
  153. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_server.py +0 -0
  154. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/mcp_server/test_session.py +0 -0
  155. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/__init__.py +0 -0
  156. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_allocate_free.py +0 -0
  157. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_bufflength_inference.py +0 -0
  158. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_editor.py +0 -0
  159. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_narrow_width_consistency.py +0 -0
  160. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_partial_io.py +0 -0
  161. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_read_into.py +0 -0
  162. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_search_argument_errors.py +0 -0
  163. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_str_boundary.py +0 -0
  164. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_str_decode_consistency.py +0 -0
  165. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_typed_accessors.py +0 -0
  166. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_unaligned_scan.py +0 -0
  167. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_write_span.py +0 -0
  168. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/memory/test_write_str_bytes_width.py +0 -0
  169. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/__init__.py +0 -0
  170. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_bitness.py +0 -0
  171. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_bitness_fallback_warns.py +0 -0
  172. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_closed_generators.py +0 -0
  173. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_linux_error_hints.py +0 -0
  174. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_linux_pid_reuse.py +0 -0
  175. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_linux_proc_decoding.py +0 -0
  176. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_linux_snapshot_passthrough.py +0 -0
  177. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_linux_types.py +0 -0
  178. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_close.py +0 -0
  179. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_load_commands.py +0 -0
  180. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_protect.py +0 -0
  181. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_region_walk.py +0 -0
  182. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_snapshot.py +0 -0
  183. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_threads.py +0 -0
  184. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_transient_reads.py +0 -0
  185. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_macos_types.py +0 -0
  186. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_win32_backend_guards.py +0 -0
  187. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_win32_permissions.py +0 -0
  188. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_win32_region_walk.py +0 -0
  189. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_win32_toolhelp.py +0 -0
  190. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/platforms/test_win32_wow64.py +0 -0
  191. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/pointer/__init__.py +0 -0
  192. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/pointer/test_pointer_chain.py +0 -0
  193. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/pointer/test_pointer_scan.py +0 -0
  194. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/pointer/test_remote_pointer.py +0 -0
  195. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/__init__.py +0 -0
  196. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_foreign_process.py +0 -0
  197. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_memory_region.py +0 -0
  198. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_module_enumeration.py +0 -0
  199. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_partial_name_match.py +0 -0
  200. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_process_enumeration.py +0 -0
  201. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_process_lookup.py +0 -0
  202. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/process/test_thread_enumeration.py +0 -0
  203. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/__init__.py +0 -0
  204. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/_fakes.py +0 -0
  205. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_chunking_integration.py +0 -0
  206. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_pattern_compile.py +0 -0
  207. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_pattern_scan.py +0 -0
  208. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_region_snapshot.py +0 -0
  209. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_scan.py +0 -0
  210. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_scan_numpy.py +0 -0
  211. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_scan_properties.py +0 -0
  212. {pymemoryeditor-3.3.3 → pymemoryeditor-3.4.1}/tests/scan/test_scanning_helper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: PyMemoryEditor
3
- Version: 3.3.3
3
+ Version: 3.4.1
4
4
  Summary: Read, write and scan process memory in a few lines of Python — Cheat Engine-style scans, pointer chains and AOB search on Windows, Linux and macOS.
5
5
  Project-URL: Homepage, https://github.com/JeanExtreme002/PyMemoryEditor
6
6
  Project-URL: Documentation, https://pymemoryeditor.readthedocs.io
@@ -38,7 +38,8 @@ Classifier: Typing :: Typed
38
38
  Requires-Python: >=3.10
39
39
  Provides-Extra: app
40
40
  Requires-Dist: psutil<8,>=5.9; extra == 'app'
41
- Requires-Dist: pyside6<7,>=6.5; extra == 'app'
41
+ Requires-Dist: pyside6<6.12,>=6.5; (python_version < '3.12') and extra == 'app'
42
+ Requires-Dist: pyside6<7,>=6.5; (python_version >= '3.12') and extra == 'app'
42
43
  Provides-Extra: dev
43
44
  Requires-Dist: build; extra == 'dev'
44
45
  Requires-Dist: flake8; extra == 'dev'
@@ -46,7 +47,8 @@ Requires-Dist: hypothesis; extra == 'dev'
46
47
  Requires-Dist: mcp<3,>=2; extra == 'dev'
47
48
  Requires-Dist: mypy; extra == 'dev'
48
49
  Requires-Dist: psutil<8,>=5.9; extra == 'dev'
49
- Requires-Dist: pyside6<7,>=6.5; extra == 'dev'
50
+ Requires-Dist: pyside6<6.12,>=6.5; (python_version < '3.12') and extra == 'dev'
51
+ Requires-Dist: pyside6<7,>=6.5; (python_version >= '3.12') and extra == 'dev'
50
52
  Requires-Dist: pytest; extra == 'dev'
51
53
  Requires-Dist: pytest-cov; extra == 'dev'
52
54
  Requires-Dist: pytest-qt; extra == 'dev'
@@ -60,7 +62,8 @@ Provides-Extra: tests
60
62
  Requires-Dist: hypothesis; extra == 'tests'
61
63
  Requires-Dist: mcp<3,>=2; extra == 'tests'
62
64
  Requires-Dist: psutil<8,>=5.9; extra == 'tests'
63
- Requires-Dist: pyside6<7,>=6.5; extra == 'tests'
65
+ Requires-Dist: pyside6<6.12,>=6.5; (python_version < '3.12') and extra == 'tests'
66
+ Requires-Dist: pyside6<7,>=6.5; (python_version >= '3.12') and extra == 'tests'
64
67
  Requires-Dist: pytest; extra == 'tests'
65
68
  Requires-Dist: pytest-cov; extra == 'tests'
66
69
  Requires-Dist: pytest-qt; extra == 'tests'
@@ -8,7 +8,7 @@ Supported platforms: Windows, Linux and macOS (32-bit and 64-bit).
8
8
  """
9
9
 
10
10
  __author__ = "Jean Loui Bernard Silva de Jesus"
11
- __version__ = "3.3.3"
11
+ __version__ = "3.4.1"
12
12
 
13
13
  import logging
14
14
  import sys
@@ -196,12 +196,14 @@ class LinuxProcess(AbstractProcess):
196
196
  bufflength: Optional[int] = None,
197
197
  ) -> T:
198
198
  self.__require_target(hot_path=True)
199
+ self._require_address(address)
199
200
  return read_process_memory(
200
201
  self.pid, address, pytype, resolve_bufflength(pytype, bufflength)
201
202
  )
202
203
 
203
204
  def read_process_memory_into(self, address: int, buffer: object) -> int:
204
205
  self.__require_target(hot_path=True)
206
+ self._require_address(address)
205
207
  return read_process_memory_into(self.pid, address, buffer)
206
208
 
207
209
  def _scan_addresses(
@@ -270,6 +272,7 @@ class LinuxProcess(AbstractProcess):
270
272
  value: Union[bool, int, float, str, bytes] = UNSET,
271
273
  ) -> Union[bool, int, float, str, bytes]:
272
274
  self.__require_target()
275
+ self._require_address(address)
273
276
  w_pytype, w_length, w_value = prepare_write(pytype, bufflength, value)
274
277
  write_process_memory(self.pid, address, w_pytype, w_length, w_value)
275
278
  return value
@@ -255,12 +255,14 @@ class MacProcess(AbstractProcess):
255
255
  bufflength: Optional[int] = None,
256
256
  ) -> T:
257
257
  self._require_open()
258
+ self._require_address(address)
258
259
  return read_process_memory(
259
260
  self.__task, address, pytype, resolve_bufflength(pytype, bufflength)
260
261
  )
261
262
 
262
263
  def read_process_memory_into(self, address: int, buffer: object) -> int:
263
264
  self._require_open()
265
+ self._require_address(address)
264
266
  return read_process_memory_into(self.__task, address, buffer)
265
267
 
266
268
  def write_process_memory(
@@ -296,6 +298,7 @@ class MacProcess(AbstractProcess):
296
298
  :param value: value to be written.
297
299
  """
298
300
  self._require_open()
301
+ self._require_address(address)
299
302
  w_pytype, w_length, w_value = prepare_write(pytype, bufflength, value)
300
303
  write_process_memory(self.__task, address, w_pytype, w_length, w_value)
301
304
  return value
@@ -308,6 +311,7 @@ class MacProcess(AbstractProcess):
308
311
 
309
312
  def free_memory(self, address: int, size: int = 0) -> bool:
310
313
  self._require_open()
314
+ self._require_address(address)
311
315
  # mach_vm_deallocate needs the exact size. Reuse the tracked size when
312
316
  # the caller doesn't supply one.
313
317
  actual_size = size or self.__allocations.get(address, 0)
@@ -424,6 +424,10 @@ def _parse_signed_int(raw: Any, field: str) -> int:
424
424
  negative = text.startswith("-")
425
425
  body = text[1:] if negative else text
426
426
  try:
427
+ # int() accepts a sign of its own, so "--5" would strip to "-5" and
428
+ # the two minuses cancel: a write of "--5" stored +5. One sign only.
429
+ if negative and body.lstrip()[:1] in ("-", "+"):
430
+ raise ValueError(body)
427
431
  value = int(body, 16) if body[:2].lower() == "0x" else int(body, 10)
428
432
  except (ValueError, IndexError):
429
433
  raise ToolError(
@@ -53,15 +53,22 @@ _POINTER_SCAN_FORMAT = "pymemoryeditor-pointerscan"
53
53
  _POINTER_SCAN_VERSION = 1
54
54
 
55
55
 
56
- class _PointerOutOfRangeError(ValueError, OSError):
57
- """A pointer chain step landed outside the address space.
56
+ class _AddressOutOfRangeError(ValueError, OSError):
57
+ """An address (or a pointer chain step) lies outside the address space.
58
58
 
59
59
  A ``ValueError`` as documented, and also an ``OSError`` because the failed
60
60
  read of the wrapped address it replaces raised one: callers that guard a
61
- stale chain with ``except OSError`` keep working.
61
+ stale chain or a bad address with ``except OSError`` keep working.
62
62
  """
63
63
 
64
64
 
65
+ # One past the highest address this host can pass to the OS. ctypes masks an
66
+ # address argument to the width of the C type it is converted to instead of
67
+ # rejecting it, so a 32-bit Python would turn 0x1_0000_1000 into 0x1000 just as
68
+ # a 64-bit one turns 2**64 + 0x1000 into 0x1000.
69
+ _HOST_ADDRESS_END = min(ADDRESS_SPACE_END, 1 << (8 * ctypes.sizeof(ctypes.c_void_p)))
70
+
71
+
65
72
  class AbstractProcess(ABC):
66
73
  """
67
74
  Abstract class to represent a process.
@@ -205,6 +212,23 @@ class AbstractProcess(ABC):
205
212
  if not self._target_alive():
206
213
  raise ProcessIDNotExistsError(self.pid)
207
214
 
215
+ @staticmethod
216
+ def _require_address(address: int) -> None:
217
+ """
218
+ Refuse an address outside ``[0, host address space)`` before it
219
+ reaches ctypes, which would mask it to the C type's width and read,
220
+ write or free a *different*, wrapped address instead of failing.
221
+ """
222
+ if not 0 <= address < _HOST_ADDRESS_END:
223
+ raise _AddressOutOfRangeError(
224
+ "address %s is outside the %d-bit address space; it would "
225
+ "wrap around to a different address."
226
+ % (
227
+ ("-0x%X" % -address) if address < 0 else ("0x%X" % address),
228
+ _HOST_ADDRESS_END.bit_length() - 1,
229
+ )
230
+ )
231
+
208
232
  @abstractmethod
209
233
  def _detect_is_64bit(self) -> Optional[bool]:
210
234
  """
@@ -736,7 +760,9 @@ class AbstractProcess(ABC):
736
760
  and ``convert_from_byte_array``. Callers that need the original
737
761
  bytes verbatim (no decoding) should pass ``pytype=bytes``.
738
762
 
739
- :raises ValueError: if ``pytype`` is unsupported.
763
+ :raises ValueError: if ``pytype`` is unsupported, or ``address`` is
764
+ negative or past the end of the address space (also an
765
+ ``OSError``, so ``except OSError`` still catches it).
740
766
  :raises OSError: if the read fails, or returns fewer bytes than
741
767
  requested (e.g. the range crosses an unreadable/freed page).
742
768
  """
@@ -770,7 +796,9 @@ class AbstractProcess(ABC):
770
796
 
771
797
  :raises TypeError: if ``buffer`` is not a writable buffer (e.g. an
772
798
  immutable ``bytes`` object).
773
- :raises ValueError: if ``buffer`` is empty or not contiguous.
799
+ :raises ValueError: if ``buffer`` is empty or not contiguous, or
800
+ ``address`` is outside the address space (as in
801
+ :meth:`read_process_memory`).
774
802
  :raises OSError: if the read fails, or returns fewer bytes than
775
803
  requested (e.g. the range crosses an unreadable/freed page).
776
804
 
@@ -821,7 +849,8 @@ class AbstractProcess(ABC):
821
849
  (``write_process_memory(address, int, 4, 99)``).
822
850
  :raises ValueError: if ``pytype`` is unsupported, or if an ``int``
823
851
  ``value`` does not fit in ``bufflength`` bytes (rejected up front
824
- rather than silently truncated).
852
+ rather than silently truncated), or if ``address`` is outside the
853
+ address space (as in :meth:`read_process_memory`).
825
854
  :raises OSError: if the write fails, or fewer bytes than requested are
826
855
  written (e.g. the range crosses an unwritable/freed page).
827
856
  :return: the original ``value`` passed in.
@@ -1080,7 +1109,8 @@ class AbstractProcess(ABC):
1080
1109
  :return: ``True`` on success.
1081
1110
 
1082
1111
  :raises ValueError: on macOS and Windows, for a region this object did
1083
- not allocate when no ``size`` is given.
1112
+ not allocate when no ``size`` is given, or for an ``address``
1113
+ outside the address space.
1084
1114
  :raises NotImplementedError: on Linux (see :meth:`allocate_memory`).
1085
1115
  """
1086
1116
  raise NotImplementedError()
@@ -1190,7 +1220,7 @@ class AbstractProcess(ABC):
1190
1220
  # it, so `ptr + offset` below zero or past 2**64 would silently
1191
1221
  # read a *different*, wrapped address. Refuse it by name instead.
1192
1222
  if not 0 <= addr < ADDRESS_SPACE_END:
1193
- raise _PointerOutOfRangeError(
1223
+ raise _AddressOutOfRangeError(
1194
1224
  "pointer chain step produced address %s, outside the "
1195
1225
  "64-bit address space; the chain is stale (a hop read "
1196
1226
  "NULL or garbage) or an offset is wrong."
@@ -303,6 +303,7 @@ class WindowsProcess(AbstractProcess):
303
303
  ) -> T:
304
304
  self._require_open()
305
305
  self.__require_read()
306
+ self._require_address(address)
306
307
  return ReadProcessMemory(
307
308
  self.__process_handle,
308
309
  address,
@@ -313,6 +314,7 @@ class WindowsProcess(AbstractProcess):
313
314
  def read_process_memory_into(self, address: int, buffer: object) -> int:
314
315
  self._require_open()
315
316
  self.__require_read()
317
+ self._require_address(address)
316
318
  return ReadProcessMemoryInto(self.__process_handle, address, buffer)
317
319
 
318
320
  def write_process_memory(
@@ -324,6 +326,7 @@ class WindowsProcess(AbstractProcess):
324
326
  ) -> Union[bool, int, float, str, bytes]:
325
327
  self._require_open()
326
328
  self.__require_write()
329
+ self._require_address(address)
327
330
  w_pytype, w_length, w_value = prepare_write(pytype, bufflength, value)
328
331
  WriteProcessMemory(
329
332
  self.__process_handle,
@@ -344,6 +347,7 @@ class WindowsProcess(AbstractProcess):
344
347
  def free_memory(self, address: int, size: int = 0) -> bool:
345
348
  self._require_open()
346
349
  self.__require_operation()
350
+ self._require_address(address)
347
351
  # MEM_RELEASE tears down whatever VirtualAlloc'd block starts at
348
352
  # `address` -- a heap segment or a thread stack just as readily as one
349
353
  # of ours -- so an address this object did not allocate is refused
@@ -23,7 +23,7 @@ copyright = f"{datetime.now().year}, {author}"
23
23
  try:
24
24
  from PyMemoryEditor import __version__ as release
25
25
  except Exception: # pragma: no cover - docs can build without the package installed
26
- release = "3.3.3"
26
+ release = "3.4.1"
27
27
 
28
28
  version = ".".join(release.split(".")[:2])
29
29
 
@@ -87,8 +87,18 @@ tests = [
87
87
  # psutil 7 removed nothing the app uses (Process.name/create_time,
88
88
  # pid_exists, process_iter with pid/name/username, and the three exceptions);
89
89
  # the app suite passes on 7.2.2.
90
+ #
91
+ # PySide6 6.12.0 is excluded below Python 3.12: its Linux wheels drop a
92
+ # reference to ``None`` on ordinary void Qt calls (upstream PYSIDE-3474), so
93
+ # a long enough session -- a CI worker that has run the app suite -- aborts
94
+ # with ``Fatal Python error: none_dealloc: deallocating None``. On 3.12+ the
95
+ # singletons are immortal (PEP 683) and the leak is harmless, which is why
96
+ # only the 3.10/3.11 cells of the matrix were exposed. The crash took the
97
+ # xdist worker down, and the session then hung until the job's 20-minute
98
+ # timeout. Lift the cap once a 6.12.x release passes the app suite on 3.11.
90
99
  app = [
91
- "PySide6>=6.5,<7",
100
+ "PySide6>=6.5,<6.12; python_version < '3.12'",
101
+ "PySide6>=6.5,<7; python_version >= '3.12'",
92
102
  "psutil>=5.9,<8",
93
103
  ]
94
104
  # The Model Context Protocol server (PyMemoryEditor.mcp), which lets an AI
@@ -158,6 +158,15 @@ class TestParseOffset:
158
158
  assert parse_offset(hex(MAX_OFFSET_MAGNITUDE)) == MAX_OFFSET_MAGNITUDE
159
159
  assert parse_offset(-MAX_OFFSET_MAGNITUDE) == -MAX_OFFSET_MAGNITUDE
160
160
 
161
+ @pytest.mark.parametrize("raw", ["--8", "-+8", "- -8"])
162
+ def test_a_doubled_sign_is_refused_not_cancelled(self, raw):
163
+ """``"--8"`` used to strip one minus and let ``int()`` read the other,
164
+ so the two cancelled and the hop walked forwards instead of back."""
165
+ from PyMemoryEditor.mcp.toolset import parse_offset
166
+
167
+ with pytest.raises(ToolError, match="Could not parse"):
168
+ parse_offset(raw)
169
+
161
170
 
162
171
  class TestNumericArgumentsReachTheModelAsToolError:
163
172
  """Only ``ToolError`` text reaches the model.
@@ -253,6 +262,17 @@ class TestParseValue:
253
262
  with pytest.raises(ToolError, match="hex"):
254
263
  parse_value(bytes, raw)
255
264
 
265
+ @pytest.mark.parametrize("raw", ["--5", "-+5", "- -5", "--0x5"])
266
+ def test_int_refuses_a_doubled_sign_instead_of_cancelling_it(self, raw):
267
+ """``"--5"`` parsed as +5, so a ``write_value`` of it stored the
268
+ opposite sign of what was sent. A typo must fail, not flip."""
269
+ with pytest.raises(ToolError, match="Could not parse"):
270
+ parse_value(int, raw)
271
+
272
+ @pytest.mark.parametrize("raw, expected", [("+5", 5), ("- 5", -5)])
273
+ def test_a_single_sign_still_parses(self, raw, expected):
274
+ assert parse_value(int, raw) == expected
275
+
256
276
  def test_int_rejects_junk_with_actionable_message(self):
257
277
  with pytest.raises(ToolError, match="0x"):
258
278
  parse_value(int, "one hundred")
@@ -672,10 +672,10 @@ class TestPointers:
672
672
  ):
673
673
  # The library's range error is both a ValueError and an OSError; it
674
674
  # must read as a stale chain, not as a malformed argument.
675
- from PyMemoryEditor.process.abstract import _PointerOutOfRangeError
675
+ from PyMemoryEditor.process.abstract import _AddressOutOfRangeError
676
676
 
677
677
  def resolve(*args, **kwargs):
678
- raise _PointerOutOfRangeError("pointer chain step produced address -0x100")
678
+ raise _AddressOutOfRangeError("pointer chain step produced address -0x100")
679
679
 
680
680
  monkeypatch.setattr(fake_process, "resolve_pointer_chain", resolve)
681
681
  with pytest.raises(ToolError) as caught:
@@ -208,3 +208,65 @@ def test_write_to_unmapped_address_raises():
208
208
  process.write_int(0x1, 7)
209
209
  finally:
210
210
  process.close()
211
+
212
+
213
+ # --------------------------------------------------------------------------- #
214
+ # (3) an address outside the address space must raise, not wrap.
215
+ # ctypes masks an address argument to 64 bits (32 on a 32-bit Python), so
216
+ # ``2**64 + addr`` used to read and write ``addr`` itself, with no error.
217
+ # --------------------------------------------------------------------------- #
218
+
219
+ _ADDRESS_WIDTH = 8 * ctypes.sizeof(ctypes.c_void_p)
220
+
221
+
222
+ def _wrapping_addresses(address):
223
+ """Addresses that ctypes would mask onto ``address``."""
224
+ return [address + (1 << _ADDRESS_WIDTH), address - (1 << _ADDRESS_WIDTH)]
225
+
226
+
227
+ @pytest.fixture
228
+ def own_process():
229
+ process = OpenProcess(pid=os.getpid())
230
+ try:
231
+ yield process
232
+ finally:
233
+ process.close()
234
+
235
+
236
+ @pytest.mark.parametrize("which", [0, 1], ids=["past-the-end", "negative"])
237
+ def test_write_to_a_wrapping_address_raises_and_leaves_memory_untouched(own_process, which):
238
+ target = ctypes.c_int32(1234)
239
+ wrapped = _wrapping_addresses(ctypes.addressof(target))[which]
240
+
241
+ with pytest.raises(ValueError, match="outside the %d-bit address space" % _ADDRESS_WIDTH):
242
+ own_process.write_process_memory(wrapped, int, 4, 4321)
243
+ with pytest.raises(ValueError, match="outside"):
244
+ own_process.write_int(wrapped, 4321)
245
+
246
+ assert target.value == 1234
247
+
248
+
249
+ @pytest.mark.parametrize("which", [0, 1], ids=["past-the-end", "negative"])
250
+ def test_read_of_a_wrapping_address_raises(own_process, which):
251
+ target = ctypes.c_int32(1234)
252
+ wrapped = _wrapping_addresses(ctypes.addressof(target))[which]
253
+
254
+ with pytest.raises(ValueError, match="outside"):
255
+ own_process.read_process_memory(wrapped, int, 4)
256
+ with pytest.raises(ValueError, match="outside"):
257
+ own_process.read_process_memory_into(wrapped, bytearray(4))
258
+
259
+
260
+ def test_out_of_range_address_is_still_an_os_error(own_process):
261
+ """A negative address used to fail inside the OS read with ``OSError``;
262
+ callers guarding a bad address with ``except OSError`` must still catch it."""
263
+ with pytest.raises(OSError, match="outside"):
264
+ own_process.read_process_memory(-1, int, 4)
265
+
266
+
267
+ def test_the_last_address_of_the_space_is_not_refused_by_the_range_check(own_process):
268
+ """The top address is in range: whatever happens there is the OS's call
269
+ (unmapped, so ``OSError``), not the range check's ``ValueError``."""
270
+ with pytest.raises(OSError) as error:
271
+ own_process.read_process_memory((1 << _ADDRESS_WIDTH) - 1, bytes, 1)
272
+ assert not isinstance(error.value, ValueError)
File without changes
File without changes
File without changes
File without changes