Use `subrepo` to link to PyDev.Debugger instead of copying it (#1714)

* Remove copy of pydevd and add subrepo script

* git subrepo clone https://github.com/fabioz/PyDev.Debugger.git src/debugpy/_vendored/pydevd

subrepo:
  subdir:   "src/debugpy/_vendored/pydevd"
  merged:   "7d6e6e68"
upstream:
  origin:   "https://github.com/fabioz/PyDev.Debugger.git"
  branch:   "main"
  commit:   "7d6e6e68"
git-subrepo:
  version:  "0.4.9"
  origin:   "???"
  commit:   "???"

* Add binskim settings to match debugpy

* git subrepo clone --force https://github.com/fabioz/PyDev.Debugger.git src/debugpy/_vendored/pydevd

subrepo:
  subdir:   "src/debugpy/_vendored/pydevd"
  merged:   "cf2e47cb"
upstream:
  origin:   "https://github.com/fabioz/PyDev.Debugger.git"
  branch:   "main"
  commit:   "cf2e47cb"
git-subrepo:
  version:  "0.4.9"
  origin:   "???"
  commit:   "???"

* Remove unnecessary string test by removing the lambda in pydevd_sys_monitoring

* Fix linter

* Put back the fix in qt_loaders

* Put back binskim flag
This commit is contained in:
Rich Chiodo 2024-10-29 13:32:30 -07:00 коммит произвёл GitHub
Родитель 8ab4ee89e9
Коммит a7d5a7ec12
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
89 изменённых файлов: 8028 добавлений и 5221 удалений

Просмотреть файл

@ -103,13 +103,14 @@ Using `pydevd_log.debug` you can add logging just about anywhere in the pydevd c
## Updating pydevd
Pydevd (at src/debugpy/_vendored/pydevd) is a copy of https://github.com/fabioz/PyDev.Debugger. We do not use a git submodule but instead just copy the source.
Pydevd (at src/debugpy/_vendored/pydevd) is a subrepo of https://github.com/fabioz/PyDev.Debugger. We use the [subrepo](https://github.com/ingydotnet/git-subrepo) to have a copy of pydevd inside of debugpy
In order to update the source, you would:
- Sync to the appropriate commit in a pydevd repo
- Diff this against the src/debugpy/_vendored/pydevd folder, being careful to not remove the edits made in the debugpy version
- Run our tests
- Make any fixes to get the tests to pass (see logging on how to debug)
- git checkout -b "branch name"
- python subrepo.py pull
- git push
- Fix any debugpy tests that are failing as a result of the pull
- Create a PR from your branch
You might need to regenerate the Cython modules after any changes. This can be done by:
@ -123,13 +124,17 @@ You might need to regenerate the Cython modules after any changes. This can be d
If you've made changes to pydevd (at src/debugpy/_vendored/pydevd), you'll want to push back changes to pydevd so as Fabio makes changes to pydevd we can continue to share updates.
To do this, you would:
- python subrepo.py branch -m "pydevd branch you want to create"
- git push -f https://github.com/fabioz/PyDev.Debugger subrepo/src/debugpy/_vendored/pydevd:$(pydevd branch you want to create)
- Create a PR from that branch
- Get Fabio's buyoff on the changes
### Setting up pydevd to be testable
Follow these steps to get pydevd testable:
- git clone https://github.com/fabioz/PyDev.Debugger (or using your own fork)
- copy all of your changes from src/debugpy/_vendored/pydevd to the root of your PyDev.Debugger clone
- remove the pdb files (pydevd doesn't ship those) if you rebuilt the attach dlls
- create an environment to test. The list of stuff in your environment is outlined [here](https://github.com/fabioz/PyDev.Debugger/blob/6cd4d431e6a794448f33a73857d479149041500a/.github/workflows/pydevd-tests-python.yml#L83).
- set PYTHONPATH=. (make sure you don't forget this part, otherwise a lot of tests will fail)

13
src/debugpy/_vendored/pydevd/.github/FUNDING.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [fabioz]
patreon: fabioz
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: [https://www.pydev.org/about.html] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

Просмотреть файл

@ -0,0 +1,35 @@
# Build the cython extensions (to check that we don't crash when they're there in debug mode).
python setup_pydevd_cython.py build_ext --inplace
curl -L https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz -o Python-3.8.3.tgz
tar -xzf Python-3.8.3.tgz
cd Python-3.8.3
mkdir debug
cd debug
../configure --with-pydebug
make
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
./python get-pip.py
./python -m pip install "pytest"
./python -m pip install "psutil"
./python -m pip install "untangle"
# Check that it worked.
./python -c "import pytest"
./python -c "import psutil"
./python -c "import untangle"
cd ..
cd ..
ls -la
./Python-3.8.3/debug/python -c "import sys;assert hasattr(sys,'gettotalrefcount')"
cd tests_python
# Although we compiled cython, all we're checking is that we don't crash (since it was built for the release env).
../Python-3.8.3/debug/python -m pytest test_debugger_json.py -k "test_case_json_change_breaks or test_remote_debugger_basic"
export PYTHONPATH=..
../Python-3.8.3/debug/python -c "import check_debug_python;check_debug_python.check() "

Просмотреть файл

@ -0,0 +1,48 @@
# Nice reference: https://github.com/tornadoweb/tornado/blob/master/.github/workflows/build.yml
# Docs: https://cibuildwheel.readthedocs.io/en/stable/options/
# Configurations are here and in pyproject.toml.
name: PyDev.Debugger [MANYLINUX] Release
on:
push:
branches:
- "release-pydev-debugger-test"
tags:
- "pydev_debugger_*"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04]
steps:
- uses: actions/checkout@v4
# Used to host cibuildwheel
- uses: actions/setup-python@v3
- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.21.2
- name: Remove .so files (will be rebuilt)
run: rm pydevd_attach_to_process/*.so
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_SKIP: pp* cp36-* cp37-*
CIBW_BUILD_VERBOSITY: 1
- uses: actions/upload-artifact@v3
with:
path: ./wheelhouse/*.whl
- name: Upload to PyPI .whl
run: |
pip install twine
twine upload wheelhouse/*.whl
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_KEY }}

Просмотреть файл

@ -0,0 +1,67 @@
name: PyDev.Debugger [Windows, MacOS] Release
on:
push:
branches:
- "release-pydev-debugger-test"
tags:
- "pydev_debugger_*"
env:
DISPLAY: ":99"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Display Python version
run: python --version
- name: Install common Python deps
run: |
pip install --upgrade pip
pip install wheel "cython>3" setuptools psutil twine --no-warn-script-location
- name: Build cython
env:
PYTHONPATH: .
PYDEVD_USE_CYTHON: yes
run: python build_tools/build.py
- name: Check cython unchanged
env:
PYTHONPATH: .
PYDEVD_USE_CYTHON: yes
run: python build_tools/check_no_git_modifications.py
- name: Create sdist
run: python setup.py sdist bdist_wheel
- uses: actions/upload-artifact@v3
with:
name: dist-${{ matrix.os }}-${{ matrix.python-version }}
path: dist/*
- name: Upload to PyPI .whl
run: twine upload dist/*.whl
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_KEY }}
- name: Upload to PyPI .tar.gz
if: ${{ (matrix.os == 'windows-latest') && (matrix.python-version == '3.9') }}
run: twine upload dist/*.tar.gz
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_KEY }}

Просмотреть файл

@ -0,0 +1,168 @@
name: PyDev.Debugger TESTS
on:
- push
- pull_request
env:
DISPLAY: ":99"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
name: [
"ubuntu-pypy3",
# "macos-py37-cython", -- misbehaving on github actions
"ubuntu-py38-cython-checkbin",
"windows-py39-cython",
"windows-py310-cython-checkbin",
"windows-py311-cython",
"ubuntu-py311-cython",
"ubuntu-py312-cython-checkbin",
"windows-py312-cython-checkbin",
"ubuntu-py313-cython",
"windows-py313-cython",
]
include:
- name: "ubuntu-pypy3"
python: "pypy3.10"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: NO
# - name: "macos-py37-cython"
# python: "3.7"
# os: macos-latest
# PYDEVD_USE_CYTHON: YES
- name: "ubuntu-py38-cython-checkbin"
python: "3.8"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: YES
- name: "windows-py39-cython"
python: "3.9"
os: windows-latest
PYDEVD_USE_CYTHON: YES
- name: "windows-py310-cython-checkbin"
python: "3.10"
os: windows-latest
PYDEVD_USE_CYTHON: YES
# See: https://github.com/actions/python-versions/releases
- name: "windows-py311-cython"
python: "3.11.0"
os: windows-latest
PYDEVD_USE_CYTHON: YES
- name: "ubuntu-py311-cython"
python: "3.11.0"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: YES
- name: "ubuntu-py312-cython-checkbin"
python: "3.12.0"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: YES
- name: "windows-py312-cython-checkbin"
python: "3.12"
os: windows-latest
PYDEVD_USE_CYTHON: YES
- name: "ubuntu-py313-cython"
python: "3.13"
os: ubuntu-20.04
PYDEVD_USE_CYTHON: YES
- name: "windows-py313-cython"
python: "3.13"
os: windows-latest
PYDEVD_USE_CYTHON: YES
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: Install gdb/xvfb/ptrace_scope
run: |
sudo apt-get update
sudo apt-get install gdb
sudo sysctl kernel.yama.ptrace_scope=0
sudo apt-get install xvfb
sudo apt-get install libqt5x11extras5
Xvfb -ac :99 -screen 0 1280x1024x16 > /dev/null 2>&1 &
if: contains(matrix.name, 'ubuntu')
- name: Install common Python deps
run: |
pip install --upgrade pip
pip install setuptools --no-warn-script-location
pip install wheel --no-warn-script-location
pip install "cython>3" --no-warn-script-location
pip install psutil --no-warn-script-location
pip install numpy --no-warn-script-location
pip install pytest --no-warn-script-location
pip install pytest-xdist --no-warn-script-location
pip install psutil --no-warn-script-location
pip install ipython --no-warn-script-location
pip install untangle --no-warn-script-location
pip install importlib-metadata --no-warn-script-location
- name: Install Python 3.x deps
if: contains(matrix.name, 'py3') && !contains(matrix.name, 'pypy') && !contains(matrix.name, 'py312') && !contains(matrix.name, 'py311') && !contains(matrix.name, 'py313')
run: |
pip install PySide2 --no-warn-script-location
pip install "numpy<2" --force --no-warn-script-location
pip install cherrypy --no-warn-script-location
pip install gevent==23.9.1 greenlet
- name: Install django
if: "!contains(matrix.name, 'py38')"
run: pip install "django<=4.2" --no-warn-script-location
- name: Install Pandas
if: contains(matrix.name, 'py310') && !contains(matrix.name, 'pypy')
# The pandas Styler also requires jinja2.
run: pip install pandas pyarrow jinja2 --no-warn-script-location
- name: Install Pypy 3 deps
if: contains(matrix.name, 'py3')
run: |
pip install trio
- name: Check that wheels can be built
if: contains(matrix.name, 'checkbin') && contains(matrix.name, 'ubuntu')
run: |
python -m pip install setuptools --no-warn-script-location
python -m pip install cibuildwheel==2.21.3
# Remove these .so files (will be rebuilt)
rm pydevd_attach_to_process/*.so
python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_BUILD: cp310-*manylinux*x86_64 cp311-*manylinux*x86_64 cp312-*manylinux*x86_64 cp313-*manylinux*x86_64
CIBW_BUILD_VERBOSITY: 3
- name: Rebuild .so
if: contains(matrix.name, 'checkbin') && contains(matrix.name, 'ubuntu')
run: |
pydevd_attach_to_process/linux_and_mac/compile_linux.sh
- name: Check cython unchanged
if: contains(matrix.name, 'checkbin')
env:
PYTHONPATH: .
run: |
python build_tools/build.py
python build_tools/check_no_git_modifications.py
- name: Create cython binaries
if: contains(matrix.name, 'cython')
run: |
python setup_pydevd_cython.py build_ext --inplace
- name: Check debug
if: contains(matrix.name, 'checkdebug')
run: |
./.github/install_and_run_debug_py.sh
- name: Run Python 3.x tests
env:
# QT_DEBUG_PLUGINS: 1
PYTHONPATH: .
PYDEVD_USE_CYTHON: ${{matrix.PYDEVD_USE_CYTHON }}
run: |
python -m pytest -n auto -rfE

Просмотреть файл

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme
;
[subrepo]
remote = https://github.com/fabioz/PyDev.Debugger.git
branch = main
commit = cf2e47cbb81a7b4e159f10d56208f4d22ff5423d
parent = 942a2276127598ef84d06b7f7b889281c1047712
method = merge
cmdver = 0.4.9

Просмотреть файл

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PyDev.Debugger</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
<path>/${PROJECT_DIR_NAME}/build_tools</path>
<path>/${PROJECT_DIR_NAME}/jython_test_deps/ant.jar</path>
<path>/${PROJECT_DIR_NAME}/jython_test_deps/junit.jar</path>
<path>/${PROJECT_DIR_NAME}/pydevd_attach_to_process</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_ADDITIONAL_GRAMMAR_VALIDATION">3.6</pydev_property>
</pydev_project>

Просмотреть файл

@ -0,0 +1,46 @@
eclipse.preferences.version=1
encoding//.settings/org.python.pydev.yaml=UTF-8
encoding//_pydevd_bundle/_debug_adapter/pydevd_schema.py=utf-8
encoding//pydev_ipython/inputhook.py=utf-8
encoding//pydev_ipython/inputhookglut.py=utf-8
encoding//pydev_ipython/inputhookgtk.py=utf-8
encoding//pydev_ipython/inputhookgtk3.py=utf-8
encoding//pydev_ipython/inputhookpyglet.py=utf-8
encoding//pydev_ipython/inputhookqt4.py=utf-8
encoding//pydev_ipython/inputhookqt5.py=utf-8
encoding//pydev_ipython/inputhooktk.py=utf-8
encoding//pydev_ipython/inputhookwx.py=utf-8
encoding//pydev_ipython/version.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/breakpoint.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/crash.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/interactive.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/process.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/thread.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/util.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/__init__.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/advapi32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/context_amd64.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/context_i386.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/dbghelp.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/defines.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/gdi32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/kernel32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/ntdll.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/peb_teb.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/psapi.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/shell32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/shlwapi.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/user32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/version.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/wtsapi32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/window.py=utf-8
encoding//tests_python/debugger_fixtures.py=utf-8
encoding//tests_python/resources/_debugger_case_redirect.py=utf-8
encoding//tests_python/test_collect_bytecode_info.py=utf-8
encoding//tests_python/test_convert_utilities.py=utf-8
encoding//tests_python/test_debugger.py=utf-8
encoding//tests_python/test_debugger_json.py=utf-8
encoding//tests_python/test_extract_token.py=utf-8
encoding//tests_python/test_frame_utils.py=utf-8
encoding//tests_python/test_pydev_monkey.py=utf-8
encoding//tests_python/test_safe_repr.py=utf-8

Просмотреть файл

@ -0,0 +1,52 @@
ADD_NEW_LINE_AT_END_OF_FILE: true
AUTOPEP8_PARAMETERS: ''
AUTO_ADD_SELF: true
AUTO_BRACES: true
AUTO_COLON: true
AUTO_DEDENT_ELSE: true
AUTO_INDENT_AFTER_PAR_WIDTH: 1
AUTO_INDENT_TO_PAR_LEVEL: false
AUTO_LINK: false
AUTO_LITERALS: true
AUTO_PAR: true
AUTO_WRITE_IMPORT_STR: true
BLACK_FORMATTER_FILE_LOCATION: ''
BLACK_FORMATTER_LOCATION_OPTION: LOCATION_SEARCH
BLACK_PARAMETERS: ''
BLANK_LINES_INNER: 1
BLANK_LINES_TOP_LEVEL: 2
BREAK_IMPORTS_MODE: PARENTHESIS
DATE_FIELD_FORMAT: yyyy-MM-dd
DATE_FIELD_NAME: __updated__
DELETE_UNUSED_IMPORTS: false
ENABLE_DATE_FIELD_ACTION: false
FORMATTER_STYLE: RUFF
FORMAT_BEFORE_SAVING: true
FORMAT_ONLY_CHANGED_LINES: false
FORMAT_WITH_AUTOPEP8: false
FROM_IMPORTS_FIRST: false
GROUP_IMPORTS: true
IMPORT_ENGINE: IMPORT_ENGINE_PEP_8
INDENT_AFTER_PAR_AS_PEP8: false
MANAGE_BLANK_LINES: true
MULTILINE_IMPORTS: true
PEP8_IMPORTS: true
PYDEV_TEST_RUNNER: '2'
PYDEV_TEST_RUNNER_DEFAULT_PARAMETERS: --capture=no -vv --tb=native -n 0
PYDEV_USE_PYUNIT_VIEW: true
RUFF_FORMATTER_FILE_LOCATION: ''
RUFF_FORMATTER_LOCATION_OPTION: LOCATION_SEARCH
RUFF_PARAMETERS: ''
SAVE_ACTIONS_ONLY_ON_WORKSPACE_FILES: true
SMART_INDENT_PAR: true
SMART_LINE_MOVE: false
SORT_IMPORTS_ON_SAVE: false
SORT_NAMES_GROUPED: false
SPACES_BEFORE_COMMENT: '2'
SPACES_IN_START_COMMENT: '1'
TRIM_EMPTY_LINES: true
TRIM_MULTILINE_LITERALS: true
USE_ASSIGN_WITH_PACES_INSIDER_PARENTESIS: false
USE_OPERATORS_WITH_SPACE: true
USE_SPACE_AFTER_COMMA: true
USE_SPACE_FOR_PARENTESIS: false

Просмотреть файл

@ -1,8 +1,8 @@
include *.rst *.txt *.md LICENSE-EPL LICENSE-APACHE *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/common *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/linux_and_mac *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/winappdbg *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/windows *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include _pydevd_bundle *.pyx *.cpp *.hpp
include *.rst *.txt *.md LICENSE-EPL LICENSE-APACHE *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/common *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/linux_and_mac *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/winappdbg *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include pydevd_attach_to_process/windows *.py *.dll *.so *.dylib *.txt *.c *.h *.bat Makefile *.sh *.pyx *.cpp *.hpp
recursive-include _pydevd_bundle *.pyx *.cpp *.hpp
recursive-include build_tools *.py

Просмотреть файл

@ -4,7 +4,14 @@ from _pydev_bundle._pydev_saved_modules import threading
# circumstances).
# It is required to debug threads started by start_new_thread in Python 3.4
_temp = threading.Thread()
if hasattr(_temp, "_is_stopped"): # Python 3.12 and earlier has this
if hasattr(_temp, "_handle") and hasattr(_temp, "_started"): # Python 3.13 and later has this
def is_thread_alive(t):
return not t._handle.is_done()
elif hasattr(_temp, "_is_stopped"): # Python 3.12 and earlier has this
def is_thread_alive(t):
return not t._is_stopped

Просмотреть файл

@ -12,17 +12,14 @@ from _pydevd_bundle.pydevd_constants import (
set_global_debugger,
DebugInfoHolder,
PYDEVD_USE_SYS_MONITORING,
IS_PY313_OR_GREATER,
)
from _pydev_bundle import pydev_log
from contextlib import contextmanager
from _pydevd_bundle import pydevd_constants, pydevd_defaults
from _pydevd_bundle.pydevd_defaults import PydevdCustomization
import ast
try:
from pathlib import Path
except ImportError:
Path = None
from pathlib import Path
# ===============================================================================
# Things that are dependent on having the pydevd debugger
@ -299,7 +296,7 @@ def remove_quotes_from_args(args):
new_args = []
for x in args:
if Path is not None and isinstance(x, Path):
if isinstance(x, Path):
x = str(x)
else:
if not isinstance(x, (bytes, str)):
@ -316,7 +313,7 @@ def remove_quotes_from_args(args):
else:
new_args = []
for x in args:
if Path is not None and isinstance(x, Path):
if isinstance(x, Path):
x = x.as_posix()
else:
if not isinstance(x, (bytes, str)):
@ -1173,15 +1170,31 @@ threading_modules_to_patch = _get_threading_modules_to_patch()
def patch_thread_module(thread_module):
if getattr(thread_module, "_original_start_new_thread", None) is None:
if thread_module is threading:
if not hasattr(thread_module, "_start_new_thread"):
return # Jython doesn't have it.
_original_start_new_thread = thread_module._original_start_new_thread = thread_module._start_new_thread
# Note: this is needed not just for the tracing, but to have an early way to
# notify that a thread was created (i.e.: tests_python.test_debugger_json.test_case_started_exited_threads_protocol)
start_thread_attrs = ["_start_new_thread", "start_new_thread", "start_new"]
start_joinable_attrs = ["start_joinable_thread", "_start_joinable_thread"]
check = start_thread_attrs + start_joinable_attrs
replace_attrs = []
for attr in check:
if hasattr(thread_module, attr):
replace_attrs.append(attr)
if not replace_attrs:
return
for attr in replace_attrs:
if attr in start_joinable_attrs:
if getattr(thread_module, "_original_start_joinable_thread", None) is None:
_original_start_joinable_thread = thread_module._original_start_joinable_thread = getattr(thread_module, attr)
else:
_original_start_joinable_thread = thread_module._original_start_joinable_thread
else:
_original_start_new_thread = thread_module._original_start_new_thread = thread_module.start_new_thread
else:
_original_start_new_thread = thread_module._original_start_new_thread
if getattr(thread_module, "_original_start_new_thread", None) is None:
_original_start_new_thread = thread_module._original_start_new_thread = getattr(thread_module, attr)
else:
_original_start_new_thread = thread_module._original_start_new_thread
class ClassWithPydevStartNewThread:
def pydev_start_new_thread(self, function, args=(), kwargs={}):
@ -1191,6 +1204,19 @@ def patch_thread_module(thread_module):
"""
return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ())
class ClassWithPydevStartJoinableThread:
def pydev_start_joinable_thread(self, function, *args, **kwargs):
"""
We need to replace the original thread_module._start_joinable_thread with this function so that threads started
through it and not through the threading module are properly traced.
"""
# Note: only handling the case from threading.py where the handle
# and daemon flags are passed explicitly. This will fail if some user library
# actually passes those without being a keyword argument!
handle = kwargs.pop("handle", None)
daemon = kwargs.pop("daemon", True)
return _original_start_joinable_thread(_UseNewThreadStartup(function, args, kwargs), handle=handle, daemon=daemon)
# This is a hack for the situation where the thread_module.start_new_thread is declared inside a class, such as the one below
# class F(object):
# start_new_thread = thread_module.start_new_thread
@ -1200,17 +1226,15 @@ def patch_thread_module(thread_module):
# So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it
# does work in the default case because in builtins self isn't passed either.
pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread
pydev_start_joinable_thread = ClassWithPydevStartJoinableThread().pydev_start_joinable_thread
try:
# We need to replace the original thread_module.start_new_thread with this function so that threads started through
# it and not through the threading module are properly traced.
if thread_module is threading:
thread_module._start_new_thread = pydev_start_new_thread
# We need to replace the original thread_module.start_new_thread with this function so that threads started through
# it and not through the threading module are properly traced.
for attr in replace_attrs:
if attr in start_joinable_attrs:
setattr(thread_module, attr, pydev_start_joinable_thread)
else:
thread_module.start_new_thread = pydev_start_new_thread
thread_module.start_new = pydev_start_new_thread
except:
pass
setattr(thread_module, attr, pydev_start_new_thread)
def patch_thread_modules():
@ -1235,6 +1259,16 @@ def undo_patch_thread_modules():
except:
pass
try:
t._start_joinable_thread = t._original_start_joinable_thread
except:
pass
try:
t.start_joinable_thread = t._original_start_joinable_thread
except:
pass
def disable_trace_thread_modules():
"""

Просмотреть файл

@ -846,6 +846,8 @@ class _Disassembler(object):
argrepr = instruction.argrepr
if isinstance(argrepr, str) and argrepr.startswith("NULL + "):
argrepr = argrepr[7:]
if isinstance(argrepr, str) and argrepr.endswith("+ NULL"):
argrepr = argrepr[:-7]
return _MsgPart(line, tok if tok is not None else dec(instruction, argrepr))
def _next_instruction_to_str(self, line_to_contents):

Просмотреть файл

@ -1140,7 +1140,8 @@ def internal_get_next_statement_targets(dbg, seq, thread_id, frame_id):
xml += "<line>%d</line>" % (frame.f_lineno,)
else:
for _, line in linestarts:
xml += "<line>%d</line>" % (line,)
if line is not None:
xml += "<line>%d</line>" % (line,)
del frame
xml += "</xml>"
cmd = dbg.cmd_factory.make_get_next_statement_targets_message(seq, xml)
@ -1342,9 +1343,10 @@ def internal_evaluate_expression(dbg, seq, thread_id, frame_id, expression, is_e
dbg.writer.add_command(cmd)
def _set_expression_response(py_db, request, result, error_message):
body = pydevd_schema.SetExpressionResponseBody(result="", variablesReference=0)
variables_response = pydevd_base_schema.build_response(request, kwargs={"body": body, "success": False, "message": error_message})
def _set_expression_response(py_db, request, error_message):
body = pydevd_schema.SetExpressionResponseBody(value='')
variables_response = pydevd_base_schema.build_response(request, kwargs={
'body':body, 'success':False, 'message': error_message})
py_db.writer.add_command(NetCommand(CMD_RETURN, 0, variables_response, is_json=True))
@ -1360,19 +1362,18 @@ def internal_set_expression_json(py_db, request, thread_id):
fmt = fmt.to_dict()
frame = py_db.find_frame(thread_id, frame_id)
exec_code = "%s = (%s)" % (expression, value)
result = pydevd_vars.evaluate_expression(py_db, frame, exec_code, is_exec=True)
is_error = isinstance(result, ExceptionOnEvaluate)
if is_error:
_set_expression_response(py_db, request, result, error_message="Error executing: %s" % (exec_code,))
exec_code = '%s = (%s)' % (expression, value)
try:
pydevd_vars.evaluate_expression(py_db, frame, exec_code, is_exec=True)
except (Exception, KeyboardInterrupt):
_set_expression_response(py_db, request, error_message='Error executing: %s' % (exec_code,))
return
# Ok, we have the result (could be an error), let's put it into the saved variables.
frame_tracker = py_db.suspended_frames_manager.get_frame_tracker(thread_id)
if frame_tracker is None:
# This is not really expected.
_set_expression_response(py_db, request, result, error_message="Thread id: %s is not current thread id." % (thread_id,))
_set_expression_response(py_db, request, error_message='Thread id: %s is not current thread id.' % (thread_id,))
return
# Now that the exec is done, get the actual value changed to return.

Просмотреть файл

@ -1,6 +1,7 @@
"""
This module holds the constants used for specifying the states of the debugger.
"""
from __future__ import nested_scopes
import platform
import weakref
@ -176,6 +177,13 @@ IS_PY312_OR_GREATER = sys.version_info >= (3, 12)
IS_PY313_OR_GREATER = sys.version_info >= (3, 13)
IS_PY314_OR_GREATER = sys.version_info >= (3, 14)
# Bug affecting Python 3.13.0 specifically makes some tests crash the interpreter!
# Hopefully it'll be fixed in 3.13.1.
IS_PY313_0 = sys.version_info[:3] == (3, 13, 0)
# Mark tests that need to be fixed with this.
TODO_PY313_OR_GREATER = IS_PY313_OR_GREATER
# Not currently supported in Python 3.14.
SUPPORT_ATTACH_TO_PID = not IS_PY314_OR_GREATER

Просмотреть файл

@ -101,6 +101,7 @@ def _patch_threading_to_hide_pydevd_threads():
{"_active_limbo_lock", "_limbo", "_active", "values", "list"},
{"_active_limbo_lock", "_limbo", "_active", "values", "NULL + list"},
{"NULL + list", "_active", "_active_limbo_lock", "NULL|self + values", "_limbo"},
{'_active_limbo_lock', 'values + NULL|self', '_limbo', '_active', 'list + NULL'},
):
pydev_log.debug("Applying patching to hide pydevd threads (Py3 version).")

Просмотреть файл

@ -1,4 +1,4 @@
from _pydevd_bundle.pydevd_constants import EXCEPTION_TYPE_USER_UNHANDLED, EXCEPTION_TYPE_UNHANDLED, IS_PY311_OR_GREATER
from _pydevd_bundle.pydevd_constants import EXCEPTION_TYPE_USER_UNHANDLED, EXCEPTION_TYPE_UNHANDLED, IS_PY311_OR_GREATER, IS_PY313_0
from _pydev_bundle import pydev_log
import itertools
from typing import Any, Dict
@ -34,47 +34,50 @@ def add_exception_to_frame(frame, exception_info):
def remove_exception_from_frame(frame):
# In 3.13 frame.f_locals became a proxy for a dict, so we need to copy it to a real dict
# so we can call the defined update method. Just deleting the entry throws in 3.13.
items = {key: value for key, value in frame.f_locals.items()}
if "__exception__" in items:
del items["__exception__"]
frame.f_locals.update(items)
if IS_PY313_0:
# In 3.13.0 frame.f_locals became a proxy for a dict, It does not
# have methods to allow items to be removed, only added. So just set the item to None.
# Should be fixed in 3.13.1 in PR: https://github.com/python/cpython/pull/125616
frame.f_locals["__exception__"] = None
else:
frame.f_locals.pop("__exception__", None)
FILES_WITH_IMPORT_HOOKS = ["pydev_monkey_qt.py", "pydev_import_hook.py"]
def just_raised(trace):
if trace is None:
return False
return trace.tb_next is None
def short_tb(exc_tb):
traceback = []
while exc_tb:
traceback.append('{%r, %r, %r}' % (exc_tb.tb_frame.f_code.co_filename,
exc_tb.tb_frame.f_code.co_name,
exc_tb.tb_lineno))
traceback.append("{%r, %r, %r}" % (exc_tb.tb_frame.f_code.co_filename, exc_tb.tb_frame.f_code.co_name, exc_tb.tb_lineno))
exc_tb = exc_tb.tb_next
return 'Traceback: %s\n' % (' -> '.join(traceback))
return "Traceback: %s\n" % (" -> ".join(traceback))
def short_frame(frame):
if frame is None:
return 'None'
return "None"
filename = frame.f_code.co_filename
name = splitext(basename(filename))[0]
return '%s::%s %s' % (name, frame.f_code.co_name, frame.f_lineno)
line = hasattr(frame, "f_lineno") and frame.f_lineno or 1
return "%s::%s %s" % (name, frame.f_code.co_name, line)
def short_stack(frame):
stack = []
while frame:
stack.append(short_frame(frame))
frame = frame.f_back
return 'Stack: %s\n' % (' -> '.join(stack))
frame = frame.f_back if hasattr(frame, "f_back") else None
return "Stack: %s\n" % (" -> ".join(stack))
def ignore_exception_trace(trace):
while trace is not None:

Просмотреть файл

@ -2,6 +2,8 @@
Utility for saving locals.
"""
import sys
from _pydevd_bundle.pydevd_constants import IS_PY313_OR_GREATER
from _pydev_bundle import pydev_log
try:
import types
@ -54,6 +56,11 @@ def make_save_locals_impl():
return save_locals_pypy_impl
if IS_PY313_OR_GREATER:
# No longer needed in Python 3.13 (deprecated)
# See PEP 667
return None
try:
import ctypes
@ -108,8 +115,16 @@ def update_globals_and_locals(updated_globals, initial_globals, frame):
for key in removed:
try:
del f_locals[key]
except KeyError:
pass
except Exception:
# Python 3.13.0 has issues here:
# https://github.com/python/cpython/pull/125616
# This should be backported from the pull request
# but we still need to handle it in this version
try:
if key in f_locals:
f_locals[key] = None
except Exception as e:
pydev_log.info('Unable to remove key: %s from locals. Exception: %s', key, e)
if f_locals is not None:
save_locals(frame)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Red Hat.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1 @@
pip

Просмотреть файл

@ -0,0 +1,77 @@
Metadata-Version: 2.1
Name: bytecode
Version: 0.13.0.dev0
Summary: Python module to generate and modify bytecode
Home-page: https://github.com/MatthieuDartiailh/bytecode
Author: Victor Stinner
Author-email: victor.stinner@gmail.com
Maintainer: Matthieu C. Dartiailh
Maintainer-email: m.dartiailh@gmail.com
License: MIT license
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.6
********
bytecode
********
.. image:: https://img.shields.io/pypi/v/bytecode.svg
:alt: Latest release on the Python Cheeseshop (PyPI)
:target: https://pypi.python.org/pypi/bytecode
.. image:: https://github.com/MatthieuDartiailh/bytecode/workflows/Continuous%20Integration/badge.svg
:target: https://github.com/MatthieuDartiailh/bytecode/actions
:alt: Continuous integration
.. image:: https://github.com/MatthieuDartiailh/bytecode/workflows/Documentation%20building/badge.svg
:target: https://github.com/MatthieuDartiailh/bytecode/actions
:alt: Documentation building
.. image:: https://img.shields.io/codecov/c/github/MatthieuDartiailh/bytecode/master.svg
:alt: Code coverage of bytecode on codecov.io
:target: https://codecov.io/github/MatthieuDartiailh/bytecode
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:alt: Code formatted using Black
:target: https://github.com/psf/black
``bytecode`` is a Python module to generate and modify bytecode.
* `bytecode project homepage at GitHub
<https://github.com/MatthieuDartiailh/bytecode>`_ (code, bugs)
* `bytecode documentation
<https://bytecode.readthedocs.io/>`_
* `Download latest bytecode release at the Python Cheeseshop (PyPI)
<https://pypi.python.org/pypi/bytecode>`_
Install bytecode: ``python3 -m pip install bytecode``. It requires Python 3.6
or newer. The latest release that supports Python 3.5 is 0.12.0. For Python 2.7
support, have a look at `dead-bytecode
<https://github.com/p403n1x87/dead-bytecode>`_ instead.
Example executing ``print('Hello World!')``:
.. code:: python
from bytecode import Instr, Bytecode
bytecode = Bytecode([Instr("LOAD_NAME", 'print'),
Instr("LOAD_CONST", 'Hello World!'),
Instr("CALL_FUNCTION", 1),
Instr("POP_TOP"),
Instr("LOAD_CONST", None),
Instr("RETURN_VALUE")])
code = bytecode.to_code()
exec(code)

Просмотреть файл

@ -0,0 +1,42 @@
bytecode-0.13.0.dev0.dist-info/COPYING,sha256=baWkm-Te2LLURwK7TL0zOkMSVjVCU_ezvObHBo298Tk,1074
bytecode-0.13.0.dev0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
bytecode-0.13.0.dev0.dist-info/METADATA,sha256=9XadDK6YTQ-FPowYI5DS4ieA7hRGnRP_fM5Z9ioPkEQ,2929
bytecode-0.13.0.dev0.dist-info/RECORD,,
bytecode-0.13.0.dev0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
bytecode-0.13.0.dev0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
bytecode-0.13.0.dev0.dist-info/direct_url.json,sha256=s58Rb4KXRlMKxk-mzpvr_tJRQ-Hx8-DHsU6NdohCnAg,93
bytecode-0.13.0.dev0.dist-info/top_level.txt,sha256=9BhdB7HqYZ-PvHNoWX6ilwLYWQqcgEOLwdb3aXm5Gys,9
bytecode/__init__.py,sha256=d-yk4Xh4SwOWq9NgoD2rmBLG6RhUFNljeqs-NjMNSYM,3885
bytecode/__pycache__/__init__.cpython-38.pyc,,
bytecode/__pycache__/bytecode.cpython-38.pyc,,
bytecode/__pycache__/cfg.cpython-38.pyc,,
bytecode/__pycache__/concrete.cpython-38.pyc,,
bytecode/__pycache__/flags.cpython-38.pyc,,
bytecode/__pycache__/instr.cpython-38.pyc,,
bytecode/__pycache__/peephole_opt.cpython-38.pyc,,
bytecode/bytecode.py,sha256=IMCcatHMtQ7M31nwj4r3drcvQuGVJAOP0d7C0O8P_SE,6894
bytecode/cfg.py,sha256=RmJGJqwCxR-XYaPH9YGY4wNDycdtLvIBJb1OGSmxcN0,15274
bytecode/concrete.py,sha256=0eb6Yh_NDLmzJNcMs2TFom0EqFVSM1cO3inMH90YE-s,21683
bytecode/flags.py,sha256=hAvM_B2yQKRw44leHP0oCae0aaJraAbDDTpqIf4I1CM,5987
bytecode/instr.py,sha256=HYc65LjNSOB3GCWkNkCSkee1rRzUyr89rgdjbKBaTpE,11616
bytecode/peephole_opt.py,sha256=W-cFVPOZN-JKfDV3aImsYenDSZkSNBDTVQqeMrGPU18,15712
bytecode/tests/__init__.py,sha256=BAdOXXNRdMVX4D8TuRYPlG9PHU7Cb0bzvyfA9s435kM,4968
bytecode/tests/__pycache__/__init__.cpython-38.pyc,,
bytecode/tests/__pycache__/test_bytecode.cpython-38.pyc,,
bytecode/tests/__pycache__/test_cfg.cpython-38.pyc,,
bytecode/tests/__pycache__/test_code.cpython-38.pyc,,
bytecode/tests/__pycache__/test_concrete.cpython-38.pyc,,
bytecode/tests/__pycache__/test_flags.cpython-38.pyc,,
bytecode/tests/__pycache__/test_instr.cpython-38.pyc,,
bytecode/tests/__pycache__/test_misc.cpython-38.pyc,,
bytecode/tests/__pycache__/test_peephole_opt.cpython-38.pyc,,
bytecode/tests/__pycache__/util_annotation.cpython-38.pyc,,
bytecode/tests/test_bytecode.py,sha256=buvtlDC0NwoQ3zuZ7OENIIDngSqtiO9WkAa2-UvxGkI,15584
bytecode/tests/test_cfg.py,sha256=c0xT8OfV-mDHu-DIDWr6LVlZQyK4GfgLSmT5AsodbMk,28194
bytecode/tests/test_code.py,sha256=XCOH29rOXSoQz130s-AIC62r23e9qNjk8Y2xDB2LmSc,2100
bytecode/tests/test_concrete.py,sha256=qT2qvabkF0yC7inniNx53cMSDN-2Qi0IE3pwBZSzF8g,49253
bytecode/tests/test_flags.py,sha256=DY9U3c6tJdxJFm0jEm_To1Cc0I99EidQv_0guud-4oE,5684
bytecode/tests/test_instr.py,sha256=rYeF8u-L0aW8bLPBxTUSy_T7KP6SaXyJKv9OhC8k6aA,11295
bytecode/tests/test_misc.py,sha256=wyK1wpVPHRfaXgo-EqUI-F1nyB9-UACerHsHbExAo1U,6758
bytecode/tests/test_peephole_opt.py,sha256=niUfhgEbiFR7IAmdQ_N9Qgh7D3wdRQ_zS0V8mKC4EzI,32640
bytecode/tests/util_annotation.py,sha256=wKq6yPWrzkNlholl5Y10b3VjuCkoiYVgvcIjk_8jzf8,485

Просмотреть файл

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.36.2)
Root-Is-Purelib: true
Tag: py3-none-any

Просмотреть файл

@ -0,0 +1 @@
{"archive_info": {}, "url": "https://github.com/MatthieuDartiailh/bytecode/archive/main.zip"}

Просмотреть файл

@ -0,0 +1 @@
bytecode

Просмотреть файл

@ -13,9 +13,9 @@ from typing import Dict, Optional, Tuple, Any
from os.path import basename, splitext
from _pydev_bundle import pydev_log
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydevd_bundle import pydevd_dont_trace
from _pydevd_bundle.pydevd_constants import (
IS_PY313_OR_GREATER,
GlobalDebuggerHolder,
ForkSafeLock,
PYDEVD_IPYTHON_CONTEXT,
@ -33,7 +33,7 @@ from _pydevd_bundle.pydevd_constants import EXCEPTION_TYPE_HANDLED
from _pydevd_bundle.pydevd_trace_dispatch import is_unhandled_exception
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
from _pydevd_bundle.pydevd_dont_trace_files import PYDEV_FILE
# fmt: off
# IFDEF CYTHON
@ -60,10 +60,8 @@ _thread_local_info = threading.local()
_get_ident = threading.get_ident
_thread_active = threading._active # noqa
STATE_SUSPEND: int = 2
CMD_STEP_INTO: int = 107
CMD_STEP_OVER: int = 108
CMD_STEP_OVER_MY_CODE: int = 159
CMD_STEP_INTO_MY_CODE: int = 144
CMD_STEP_INTO_COROUTINE: int = 206
CMD_SMART_STEP_INTO: int = 128
@ -236,6 +234,7 @@ def _get_unhandled_exception_frame(exc, depth: int) -> Optional[FrameType]:
# cdef PyDBAdditionalThreadInfo additional_info
# thread: threading.Thread
# trace: bool
# _use_is_stopped: bool
# ELSE
class ThreadInfo:
additional_info: PyDBAdditionalThreadInfo
@ -256,6 +255,19 @@ class ThreadInfo:
self.thread_ident = thread_ident
self.additional_info = additional_info
self.trace = trace
self._use_is_stopped = hasattr(thread, '_is_stopped')
# fmt: off
# IFDEF CYTHON
# cdef bint is_thread_alive(self):
# ELSE
def is_thread_alive(self):
# ENDIF
# fmt: on
if self._use_is_stopped:
return not self.thread._is_stopped
else:
return not self.thread._handle.is_done()
class _DeleteDummyThreadOnDel:
@ -319,7 +331,7 @@ def _create_thread_info(depth):
if t is None:
t = _thread_active.get(thread_ident)
if isinstance(t, threading._DummyThread):
if isinstance(t, threading._DummyThread) and not IS_PY313_OR_GREATER:
_thread_local_info._ref = _DeleteDummyThreadOnDel(t)
if t is None:
@ -406,8 +418,9 @@ class FuncCodeInfo:
def get_line_of_offset(self, offset):
for start, end, line in self.code_obj.co_lines():
if offset >= start and offset <= end:
return line
if start is not None and end is not None and line is not None:
if offset >= start and offset <= end:
return line
return -1
@ -438,15 +451,41 @@ def _get_thread_info(create: bool, depth: int) -> Optional[ThreadInfo]:
return _thread_local_info.thread_info
_CodeLineInfo = namedtuple("_CodeLineInfo", "line_to_offset, first_line, last_line")
# fmt: off
# IFDEF CYTHON
# cdef class _CodeLineInfo:
# cdef dict line_to_offset
# cdef int first_line
# cdef int last_line
# ELSE
class _CodeLineInfo:
line_to_offset: Dict[int, Any]
first_line: int
last_line: int
# ENDIF
# fmt: on
# fmt: off
# IFDEF CYTHON
# def __init__(self, dict line_to_offset, int first_line, int last_line):
# self.line_to_offset = line_to_offset
# self.first_line = first_line
# self.last_line = last_line
# ELSE
def __init__(self, line_to_offset, first_line, last_line):
self.line_to_offset = line_to_offset
self.first_line = first_line
self.last_line = last_line
# ENDIF
# fmt: on
# Note: this method has a version in cython too
# fmt: off
# IFDEF CYTHON
# cdef _get_code_line_info(code_obj, _cache={}):
# cdef _CodeLineInfo _get_code_line_info(code_obj, _cache={}):
# ELSE
def _get_code_line_info(code_obj, _cache={}):
def _get_code_line_info(code_obj, _cache={}) -> _CodeLineInfo:
# ENDIF
# fmt: on
try:
@ -834,16 +873,16 @@ def _unwind_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code:
return
# pydev_log.debug('_unwind_event', code, exc)
# print('_unwind_event', code, exc)
frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)
@ -868,7 +907,6 @@ def _unwind_event(code, instruction, exc):
)
if is_unhandled:
# print('stop in user uncaught')
handle_exception(py_db, thread_info.thread, frame, user_uncaught_exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
return
@ -904,12 +942,12 @@ def _raise_event(code, instruction, exc):
thread_info = _get_thread_info(True, 1)
if thread_info is None:
return
py_db: object = GlobalDebuggerHolder.global_dbg
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -920,7 +958,6 @@ def _raise_event(code, instruction, exc):
frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)
# pydev_log.debug('_raise_event', code, exc)
# Compute the previous exception info (if any). We use it to check if the exception
# should be stopped
@ -1025,7 +1062,7 @@ def _return_event(code, instruction, retval):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1340,7 +1377,7 @@ def _jump_event(code, from_offset, to_offset):
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
del _thread_local_info.f_disable_next_line_if_match
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1358,7 +1395,6 @@ def _jump_event(code, from_offset, to_offset):
from_line = func_code_info.get_line_of_offset(from_offset or 0)
to_line = func_code_info.get_line_of_offset(to_offset or 0)
# print('jump event', code.co_name, 'from line', from_line, 'to line', to_line)
if from_line != to_line:
# I.e.: use case: "yield from [j for j in a if j % 2 == 0]"
@ -1369,6 +1405,7 @@ def _jump_event(code, from_offset, to_offset):
# Disable the next line event as we're jumping to a line. The line event will be redundant.
_thread_local_info.f_disable_next_line_if_match = (func_code_info.co_filename, frame.f_lineno)
# pydev_log.debug('_jump_event', code.co_name, 'from line', from_line, 'to line', frame.f_lineno)
return _internal_line_event(func_code_info, frame, frame.f_lineno)
@ -1397,24 +1434,26 @@ def _line_event(code, line):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
# If we get another line event, remove the extra check for the line event
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
(co_filename, line_to_skip) = _thread_local_info.f_disable_next_line_if_match
del _thread_local_info.f_disable_next_line_if_match
if line_to_skip is line and co_filename == code.co_filename:
# If we're in a jump, we should skip this line event. The jump would have
# been considered a line event for this same line and we don't want to
# The last jump already jumped to this line and we haven't had any
# line events or jumps since then. We don't want to consider this line twice
# pydev_log.debug('_line_event skipped', line)
return
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code or func_code_info.always_filtered_out:
return monitor.DISABLE
# print('line event', code.co_name, line)
# pydev_log.debug('_line_event', code.co_name, line)
# We know the frame depth.
frame = _getframe(1)
@ -1644,7 +1683,7 @@ def _start_method_event(code, instruction_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1841,7 +1880,10 @@ def update_monitor_events(suspend_requested: Optional[bool] = None) -> None:
monitor.register_callback(DEBUGGER_ID, monitor.events.PY_START, _start_method_event)
# monitor.register_callback(DEBUGGER_ID, monitor.events.PY_RESUME, _resume_method_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.LINE, _line_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.JUMP, _jump_event)
if not IS_PY313_OR_GREATER:
# In Python 3.13+ jump_events aren't necessary as we have a line_event for every
# jump location.
monitor.register_callback(DEBUGGER_ID, monitor.events.JUMP, _jump_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.PY_RETURN, _return_event)
else:

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -19,9 +19,9 @@ from typing import Dict, Optional, Tuple, Any
from os.path import basename, splitext
from _pydev_bundle import pydev_log
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydevd_bundle import pydevd_dont_trace
from _pydevd_bundle.pydevd_constants import (
IS_PY313_OR_GREATER,
GlobalDebuggerHolder,
ForkSafeLock,
PYDEVD_IPYTHON_CONTEXT,
@ -39,7 +39,7 @@ from _pydevd_bundle.pydevd_constants import EXCEPTION_TYPE_HANDLED
from _pydevd_bundle.pydevd_trace_dispatch import is_unhandled_exception
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
from _pydevd_bundle.pydevd_dont_trace_files import PYDEV_FILE
# fmt: off
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
@ -66,10 +66,8 @@ _thread_local_info = threading.local()
_get_ident = threading.get_ident
_thread_active = threading._active # noqa
STATE_SUSPEND: int = 2
CMD_STEP_INTO: int = 107
CMD_STEP_OVER: int = 108
CMD_STEP_OVER_MY_CODE: int = 159
CMD_STEP_INTO_MY_CODE: int = 144
CMD_STEP_INTO_COROUTINE: int = 206
CMD_SMART_STEP_INTO: int = 128
@ -242,6 +240,7 @@ cdef class ThreadInfo:
cdef PyDBAdditionalThreadInfo additional_info
thread: threading.Thread
trace: bool
_use_is_stopped: bool
# ELSE
# class ThreadInfo:
# additional_info: PyDBAdditionalThreadInfo
@ -262,6 +261,19 @@ cdef class ThreadInfo:
self.thread_ident = thread_ident
self.additional_info = additional_info
self.trace = trace
self._use_is_stopped = hasattr(thread, '_is_stopped')
# fmt: off
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef bint is_thread_alive(self):
# ELSE
# def is_thread_alive(self):
# ENDIF
# fmt: on
if self._use_is_stopped:
return not self.thread._is_stopped
else:
return not self.thread._handle.is_done()
class _DeleteDummyThreadOnDel:
@ -325,7 +337,7 @@ cdef _create_thread_info(depth):
if t is None:
t = _thread_active.get(thread_ident)
if isinstance(t, threading._DummyThread):
if isinstance(t, threading._DummyThread) and not IS_PY313_OR_GREATER:
_thread_local_info._ref = _DeleteDummyThreadOnDel(t)
if t is None:
@ -412,8 +424,9 @@ cdef class FuncCodeInfo:
def get_line_of_offset(self, offset):
for start, end, line in self.code_obj.co_lines():
if offset >= start and offset <= end:
return line
if start is not None and end is not None and line is not None:
if offset >= start and offset <= end:
return line
return -1
@ -444,15 +457,41 @@ cdef _get_thread_info(bint create, int depth):
return _thread_local_info.thread_info
_CodeLineInfo = namedtuple("_CodeLineInfo", "line_to_offset, first_line, last_line")
# fmt: off
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef class _CodeLineInfo:
cdef dict line_to_offset
cdef int first_line
cdef int last_line
# ELSE
# class _CodeLineInfo:
# line_to_offset: Dict[int, Any]
# first_line: int
# last_line: int
# ENDIF
# fmt: on
# fmt: off
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
def __init__(self, dict line_to_offset, int first_line, int last_line):
self.line_to_offset = line_to_offset
self.first_line = first_line
self.last_line = last_line
# ELSE
# def __init__(self, line_to_offset, first_line, last_line):
# self.line_to_offset = line_to_offset
# self.first_line = first_line
# self.last_line = last_line
#
# ENDIF
# fmt: on
# Note: this method has a version in cython too
# fmt: off
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
cdef _get_code_line_info(code_obj, _cache={}):
cdef _CodeLineInfo _get_code_line_info(code_obj, _cache={}):
# ELSE
# def _get_code_line_info(code_obj, _cache={}):
# def _get_code_line_info(code_obj, _cache={}) -> _CodeLineInfo:
# ENDIF
# fmt: on
try:
@ -840,16 +879,16 @@ cdef _unwind_event(code, instruction, exc):
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code:
return
# pydev_log.debug('_unwind_event', code, exc)
# print('_unwind_event', code, exc)
frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)
@ -874,7 +913,6 @@ cdef _unwind_event(code, instruction, exc):
)
if is_unhandled:
# print('stop in user uncaught')
handle_exception(py_db, thread_info.thread, frame, user_uncaught_exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
return
@ -910,12 +948,12 @@ cdef _raise_event(code, instruction, exc):
thread_info = _get_thread_info(True, 1)
if thread_info is None:
return
py_db: object = GlobalDebuggerHolder.global_dbg
if py_db is None or py_db.pydb_disposed:
return
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -926,7 +964,6 @@ cdef _raise_event(code, instruction, exc):
frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)
# pydev_log.debug('_raise_event', code, exc)
# Compute the previous exception info (if any). We use it to check if the exception
# should be stopped
@ -1031,7 +1068,7 @@ cdef _return_event(code, instruction, retval):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1346,7 +1383,7 @@ cdef _jump_event(code, int from_offset, int to_offset):
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
del _thread_local_info.f_disable_next_line_if_match
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1364,7 +1401,6 @@ cdef _jump_event(code, int from_offset, int to_offset):
from_line = func_code_info.get_line_of_offset(from_offset or 0)
to_line = func_code_info.get_line_of_offset(to_offset or 0)
# print('jump event', code.co_name, 'from line', from_line, 'to line', to_line)
if from_line != to_line:
# I.e.: use case: "yield from [j for j in a if j % 2 == 0]"
@ -1375,6 +1411,7 @@ cdef _jump_event(code, int from_offset, int to_offset):
# Disable the next line event as we're jumping to a line. The line event will be redundant.
_thread_local_info.f_disable_next_line_if_match = (func_code_info.co_filename, frame.f_lineno)
# pydev_log.debug('_jump_event', code.co_name, 'from line', from_line, 'to line', frame.f_lineno)
return _internal_line_event(func_code_info, frame, frame.f_lineno)
@ -1403,24 +1440,26 @@ cdef _line_event(code, int line):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
# If we get another line event, remove the extra check for the line event
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
(co_filename, line_to_skip) = _thread_local_info.f_disable_next_line_if_match
del _thread_local_info.f_disable_next_line_if_match
if line_to_skip is line and co_filename == code.co_filename:
# If we're in a jump, we should skip this line event. The jump would have
# been considered a line event for this same line and we don't want to
# The last jump already jumped to this line and we haven't had any
# line events or jumps since then. We don't want to consider this line twice
# pydev_log.debug('_line_event skipped', line)
return
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code or func_code_info.always_filtered_out:
return monitor.DISABLE
# print('line event', code.co_name, line)
# pydev_log.debug('_line_event', code.co_name, line)
# We know the frame depth.
frame = _getframe(1)
@ -1650,7 +1689,7 @@ cdef _start_method_event(code, instruction_offset):
if py_db is None or py_db.pydb_disposed:
return monitor.DISABLE
if not thread_info.trace or not is_thread_alive(thread_info.thread):
if not thread_info.trace or not thread_info.is_thread_alive():
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return
@ -1847,7 +1886,10 @@ def update_monitor_events(suspend_requested: Optional[bool] = None) -> None:
monitor.register_callback(DEBUGGER_ID, monitor.events.PY_START, _start_method_event)
# monitor.register_callback(DEBUGGER_ID, monitor.events.PY_RESUME, _resume_method_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.LINE, _line_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.JUMP, _jump_event)
if not IS_PY313_OR_GREATER:
# In Python 3.13+ jump_events aren't necessary as we have a line_event for every
# jump location.
monitor.register_callback(DEBUGGER_ID, monitor.events.JUMP, _jump_event)
monitor.register_callback(DEBUGGER_ID, monitor.events.PY_RETURN, _return_event)
else:

Просмотреть файл

@ -7,6 +7,7 @@ It should:
Note that it's used in the CI to build the cython deps based on the PYDEVD_USE_CYTHON environment variable.
"""
from __future__ import print_function
import os
@ -109,19 +110,24 @@ def build():
# set VS100COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools
if "GITHUB_ACTION" not in os.environ:
if sys.version_info[:2] in ((3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12)):
if sys.version_info[:2] in ((3, 6), (3, 7), (3, 8), (3, 9), (3, 10), (3, 11), (3, 12), (3, 13)):
FORCE_PYDEVD_VC_VARS = os.environ.get("FORCE_PYDEVD_VC_VARS")
if FORCE_PYDEVD_VC_VARS:
env.update(get_environment_from_batch_command([FORCE_PYDEVD_VC_VARS], initial=os.environ.copy()))
else:
import setuptools # We have to import it first for the compiler to be found
from distutils import msvc9compiler
try:
from setuptools._distutils._msvccompiler import _find_vcvarsall as find_vcvarsall
except Exception:
import setuptools # We have to import it first for the compiler to be found
from distutils.msvc9compiler import find_vcvarsall
vcvarsall = msvc9compiler.find_vcvarsall(14.0)
vcvarsall = find_vcvarsall(14.0)
if isinstance(vcvarsall, tuple):
vcvarsall = vcvarsall[0]
if vcvarsall is None or not os.path.exists(vcvarsall):
msvc_version = msvc9compiler.get_build_version()
print("msvc_version", msvc_version)
vcvarsall = msvc9compiler.find_vcvarsall(msvc_version)
vcvarsall = find_vcvarsall(msvc_version)
if vcvarsall is None or not os.path.exists(vcvarsall):
raise RuntimeError("Error finding vcvarsall.")

Просмотреть файл

@ -42,6 +42,17 @@ expected_differences = set(
-static const char __pyx_k_pydevd_sys_monitoring__pydevd_s[] = "_pydevd_sys_monitoring\\_pydevd_sys_monitoring_cython.pyx";
- ".\\\\\\\\_pydevd_bundle\\\\\\\\pydevd_cython.pxd",
- ".\\\\_pydevd_bundle\\\\pydevd_cython.pxd",
- "_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx",
+ "_pydevd_sys_monitoring\\\\\\\\_pydevd_sys_monitoring_cython.pyx",
- "./_pydevd_bundle/pydevd_cython.pxd",
+ ".\\\\\\\\_pydevd_bundle\\\\\\\\pydevd_cython.pxd",
-static const char __pyx_k_pydevd_sys_monitoring__pydevd_s[] = "_pydevd_sys_monitoring/_pydevd_sys_monitoring_cython.pyx";
+static const char __pyx_k_pydevd_sys_monitoring__pydevd_s[] = "_pydevd_sys_monitoring\\\\_pydevd_sys_monitoring_cython.pyx";
+ "_pydevd_sys_monitoring\\\\_pydevd_sys_monitoring_cython.pyx",
+ ".\\\\_pydevd_bundle\\\\pydevd_cython.pxd",
+static const char __pyx_k_pydevd_sys_monitoring__pydevd_s[] = "_pydevd_sys_monitoring\\_pydevd_sys_monitoring_cython.pyx";
""".splitlines()
if line.strip()
)

Просмотреть файл

@ -7,7 +7,7 @@ Update cython-generated files (must update cython and then run build_tools/build
Create tag:
-----------
git tag pydev_debugger_3_1_0 -a -m "PyDev.Debugger 3.1.0"
git tag pydev_debugger_3_2_1 -a -m "PyDev.Debugger 3.2.1"
git push --tags

Просмотреть файл

@ -130,7 +130,6 @@ def has_binding(api):
import importlib
try:
import imp
# importing top level PyQt4/PySide module is ok...
mod = __import__(module_name)
# ...importing submodules is not
@ -144,13 +143,13 @@ def has_binding(api):
return check_version(mod.__version__, "1.0.3")
else:
return True
except ModuleNotFoundError:
from importlib import machinery
# importing top level PyQt4/PySide module is ok...
mod = __import__(module_name)
# ...importing submodules is not
loader_details = (machinery.ExtensionFileLoader, machinery.EXTENSION_SUFFIXES)
submod_finder = machinery.FileFinder(mod.__path__[0], loader_details)
@ -159,7 +158,7 @@ def has_binding(api):
and submod_finder.find_spec("QtGui") is not None
and submod_finder.find_spec("QtSvg") is not None
)
# we can also safely check PySide version
if api == QT_API_PYSIDE:
return check_version(mod.__version__, '1.0.3') and submod_check

Просмотреть файл

@ -3,6 +3,7 @@ Entry point module (keep at root):
This module starts the debugger.
"""
import sys # @NoMove
if sys.version_info[:2] < (3, 6):
@ -11,14 +12,11 @@ if sys.version_info[:2] < (3, 6):
)
import os
try:
# Just empty packages to check if they're in the PYTHONPATH.
import _pydev_bundle
except ImportError:
# On the first import of a pydevd module, add pydevd itself to the PYTHONPATH
# if its dependencies cannot be imported.
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import _pydev_bundle
# On the first import of a pydevd module, add pydevd itself to the PYTHONPATH
this_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, this_dir)
import _pydev_bundle
# Import this first as it'll check for shadowed modules and will make sure that we import
# things as needed for gevent.
@ -125,7 +123,7 @@ from pydevd_file_utils import (
get_abs_path_real_path_and_base_from_file,
NORM_PATHS_AND_BASE_CONTAINER,
)
from pydevd_file_utils import get_fullname, get_package_dir, is_pydevd_path
from pydevd_file_utils import get_fullname, get_package_dir
from os.path import abspath as os_path_abspath
import pydevd_tracing
from _pydevd_bundle.pydevd_comm import InternalThreadCommand, InternalThreadCommandForAnyThread, create_server_socket, FSNotifyThread
@ -175,7 +173,7 @@ if SUPPORT_GEVENT:
if USE_CUSTOM_SYS_CURRENT_FRAMES_MAP:
from _pydevd_bundle.pydevd_constants import constructed_tid_to_last_frame
__version_info__ = (3, 1, 0)
__version_info__ = (3, 2, 2)
__version_info_str__ = []
for v in __version_info__:
__version_info_str__.append(str(v))
@ -1075,13 +1073,6 @@ class PyDB(object):
return _cache_file_type[cache_key]
except:
if abs_real_path_and_basename[0] == "<string>":
# Consider it an untraceable file unless there's no back frame (ignoring
# internal files and runpy.py).
if frame.f_back is not None and self.get_file_type(frame.f_back) == self.PYDEV_FILE and is_pydevd_path(frame.f_back.f_code.co_filename):
# Special case, this is a string coming from pydevd itself. However we have to skip this logic for other
# files that are also marked as PYDEV_FILE (like external files marked this way)
return self.PYDEV_FILE
f = frame.f_back
while f is not None:
if self.get_file_type(f) != self.PYDEV_FILE and pydevd_file_utils.basename(f.f_code.co_filename) not in (
@ -1099,7 +1090,7 @@ class PyDB(object):
# to show it in the stack.
_cache_file_type[cache_key] = LIB_FILE
return LIB_FILE
f = f.f_back
else:
# This is a top-level file (used in python -c), so, trace it as usual... we
@ -3701,5 +3692,13 @@ def main():
debugger.wait_for_commands(globals)
try:
# Remove the entry we added: it should no longer be needed as
# what we need should've been imported already
if sys.path[:1] == [this_dir]:
sys.path.remove(this_dir)
except Exception:
pass
if __name__ == "__main__":
main()

Просмотреть файл

@ -308,7 +308,7 @@ def run_python_code_windows(pid, python_code, connect_debugger_tracing=False, sh
args = [target_executable, str(pid), target_dll_run_on_dllmain]
subprocess.check_call(args)
if not event.wait_for_event_set(30):
if not event.wait_for_event_set(15):
print("Timeout error: the attach may not have completed.")
sys.stdout.flush()
print("--- Finished dll injection ---\n")

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -81,6 +81,10 @@ def fix_main_thread_id(on_warn=lambda msg: None, on_exception=lambda msg: None,
import sys
import threading
# This is no longer needed in Py 3.13 (as the related issue is already fixed).
if sys.version_info[:2] >= (3, 13):
return
try:
with threading._active_limbo_lock:
main_thread_instance = get_main_thread_instance(threading)

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичные данные
src/debugpy/_vendored/pydevd/pydevd_attach_to_process/attach_x86_64.dylib Normal file → Executable file

Двоичный файл не отображается.

Просмотреть файл

@ -127,20 +127,20 @@ int InternalSetSysTraceFunc(
pyUnicode_InternFromString = stringFromString;
}
DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
_PyObject_FastCallDict* pyObject_FastCallDict;
if (version < PythonVersion_37) {
pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
} else if (version < PythonVersion_39) {
DEFINE_PROC(fastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
pyObject_FastCallDict = fastCallDict;
} else {
DEFINE_PROC(vectorcallDict, _PyObject_FastCallDict*, "PyObject_VectorcallDict", 530);
pyObject_FastCallDict = vectorcallDict;
}
DEFINE_PROC(pyTuple_New, PyTuple_New*, "PyTuple_New", 531);
DEFINE_PROC(pyEval_CallObjectWithKeywords, PyEval_CallObjectWithKeywords*, "PyEval_CallObjectWithKeywords", 532);
if(pyObject_FastCallDict == nullptr) {
DEFINE_PROC_NO_CHECK(pyObject_VectorcallDict, _PyObject_FastCallDict*, "PyObject_VectorcallDict", 533);
pyObject_FastCallDict = pyObject_VectorcallDict;
}
if(pyObject_FastCallDict == nullptr) {
// we have to use PyObject_FastCallDictCustom for older versions of CPython (pre 3.7).
pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
}
DEFINE_PROC(pyTraceBack_Here, PyTraceBack_Here*, "PyTraceBack_Here", 540);
DEFINE_PROC(pyEval_SetTrace, PyEval_SetTrace*, "PyEval_SetTrace", 550);

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

Просмотреть файл

Просмотреть файл

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -108,8 +108,8 @@ struct InitializeThreadingInfo {
PyEval_Lock* initThreads;
CRITICAL_SECTION cs;
HANDLE initedEvent; // Note: only access with cs locked (and check if not already nullptr).
bool completed; // Note: only access with cs locked
HANDLE initedEvent; // Note: only access with mutex locked (and check if not already nullptr).
bool completed; // Note: only access with mutex locked
};
@ -322,7 +322,6 @@ extern "C"
return -240;
}
// Either _Py_CheckInterval or _PyEval_[GS]etSwitchInterval are useful, but not required
DEFINE_PROC_NO_CHECK(intervalCheck, int*, "_Py_CheckInterval", -250); // optional
DEFINE_PROC_NO_CHECK(getSwitchInterval, _PyEval_GetSwitchInterval*, "_PyEval_GetSwitchInterval", -260); // optional
@ -375,7 +374,6 @@ extern "C"
initializeThreadingInfo->initedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
InitializeCriticalSection(&initializeThreadingInfo->cs);
// Add the call to initialize threading.
addPendingCall(&AttachCallback, initializeThreadingInfo);

Просмотреть файл

@ -1,6 +1,4 @@
:: This script compiles the attach and inject DLLs for x86 and x64 architectures.
:: It runs as part of the PR checks in the debugpy repo, AND the artifacts are uploaded to github,
:: so you don't have to run it locally.
setlocal
@cd /d %~dp0

Просмотреть файл

@ -1,44 +1,44 @@
r"""
This module provides utilities to get the absolute filenames so that we can be sure that:
- The case of a file will match the actual file in the filesystem (otherwise breakpoints won't be hit).
- Providing means for the user to make path conversions when doing a remote debugging session in
one machine and debugging in another.
This module provides utilities to get the absolute filenames so that we can be sure that:
- The case of a file will match the actual file in the filesystem (otherwise breakpoints won't be hit).
- Providing means for the user to make path conversions when doing a remote debugging session in
one machine and debugging in another.
To do that, the PATHS_FROM_ECLIPSE_TO_PYTHON constant must be filled with the appropriate paths.
To do that, the PATHS_FROM_ECLIPSE_TO_PYTHON constant must be filled with the appropriate paths.
@note:
in this context, the server is where your python process is running
and the client is where eclipse is running.
@note:
in this context, the server is where your python process is running
and the client is where eclipse is running.
E.g.:
If the server (your python process) has the structure
/user/projects/my_project/src/package/module1.py
E.g.:
If the server (your python process) has the structure
/user/projects/my_project/src/package/module1.py
and the client has:
c:\my_project\src\package\module1.py
and the client has:
c:\my_project\src\package\module1.py
the PATHS_FROM_ECLIPSE_TO_PYTHON would have to be:
PATHS_FROM_ECLIPSE_TO_PYTHON = [(r'c:\my_project\src', r'/user/projects/my_project/src')]
the PATHS_FROM_ECLIPSE_TO_PYTHON would have to be:
PATHS_FROM_ECLIPSE_TO_PYTHON = [(r'c:\my_project\src', r'/user/projects/my_project/src')]
alternatively, this can be set with an environment variable from the command line:
set PATHS_FROM_ECLIPSE_TO_PYTHON=[['c:\my_project\src','/user/projects/my_project/src']]
alternatively, this can be set with an environment variable from the command line:
set PATHS_FROM_ECLIPSE_TO_PYTHON=[['c:\my_project\src','/user/projects/my_project/src']]
@note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the result of those translations
@note: DEBUG_CLIENT_SERVER_TRANSLATION can be set to True to debug the result of those translations
@note: the case of the paths is important! Note that this can be tricky to get right when one machine
uses a case-independent filesystem and the other uses a case-dependent filesystem (if the system being
debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON).
@note: the case of the paths is important! Note that this can be tricky to get right when one machine
uses a case-independent filesystem and the other uses a case-dependent filesystem (if the system being
debugged is case-independent, 'normcase()' should be used on the paths defined in PATHS_FROM_ECLIPSE_TO_PYTHON).
@note: all the paths with breakpoints must be translated (otherwise they won't be found in the server)
@note: all the paths with breakpoints must be translated (otherwise they won't be found in the server)
@note: to enable remote debugging in the target machine (pydev extensions in the eclipse installation)
import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, port, suspend)
@note: to enable remote debugging in the target machine (pydev extensions in the eclipse installation)
import pydevd;pydevd.settrace(host, stdoutToServer, stderrToServer, port, suspend)
see parameter docs on pydevd.py
see parameter docs on pydevd.py
@note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target
machine for the paths that'll actually have breakpoints).
@note: for doing a remote debugging session, all the pydevd_ files must be on the server accessible
through the PYTHONPATH (and the PATHS_FROM_ECLIPSE_TO_PYTHON only needs to be set on the target
machine for the paths that'll actually have breakpoints).
"""
from _pydev_bundle import pydev_log
@ -71,7 +71,6 @@ except:
# realpath is a no-op on systems without islink support
os_path_real_path = os.path.abspath
PYDEVD_ROOT_PATH = os_path_real_path(os.path.dirname(__file__))
def _get_library_dir():
library_dir = None
@ -964,9 +963,3 @@ def get_package_dir(mod_name):
if os.path.isdir(mod_path):
return mod_path
return None
def is_pydevd_path(path):
# Return true if this file is rooted in the pydevd directory.
dir: str = os_path_real_path(os.path.dirname(path))
return dir.startswith(PYDEVD_ROOT_PATH)

Просмотреть файл

@ -7,6 +7,7 @@
before-build = """
pydevd_attach_to_process/linux_and_mac/compile_linux.sh
pip install cython
pip install setuptools
python build_tools/build.py
"""

Просмотреть файл

@ -1,11 +0,0 @@
import unittest
import pydevd_file_utils
class TestFileUtils(unittest.TestCase):
def test_path_search(self):
self.assertTrue(pydevd_file_utils.contains_dir("foo/bar/inspect.py", "inspect.py"))
self.assertTrue(pydevd_file_utils.contains_dir("foo/bar/inspect.py", "foo"))
self.assertTrue(pydevd_file_utils.contains_dir("foo/bar/inspect.py", "bar"))
self.assertFalse(pydevd_file_utils.contains_dir("foo/bar/inspect.py", "boo"))
self.assertFalse(pydevd_file_utils.contains_dir("foo/bar/inspect.py", "foo/bar"))
self.assertFalse(pydevd_file_utils.contains_dir("<not a path>", "path"))

Просмотреть файл

@ -1,5 +1,6 @@
import sys
from _pydev_bundle._pydev_saved_modules import thread
from _pydevd_bundle.pydevd_constants import IS_PY313_OR_GREATER
import pycompletionserver
import socket
from urllib.parse import quote_plus
@ -71,6 +72,8 @@ class TestCPython(unittest.TestCase):
return msg
@unittest.skipIf(IS_PY313_OR_GREATER and sys.platform == "linux",
"Flakey on Linux")
def test_completion_sockets_and_messages(self):
t, socket = self.create_connections()
self.socket = socket

Просмотреть файл

@ -1,6 +1,7 @@
"""
@author Fabio Zadrozny
"""
from _pydev_bundle import _pydev_imports_tipper
import inspect
import pytest
@ -176,7 +177,13 @@ class TestCPython(unittest.TestCase):
self.assert_in("parseFile", tip)
else:
self.assert_args(
"parse", ["(source, filename, mode)", "(source, filename, mode, type_comments=False, feature_version=None)"], tip
"parse",
[
"(source, filename, mode)",
"(source, filename, mode, type_comments=False, feature_version=None)",
"(source, filename, mode, type_comments=False, feature_version=None, optimize=-1)",
],
tip,
)
self.assert_args("walk", "(node)", tip)
self.assert_in("parse", tip)

Просмотреть файл

@ -146,7 +146,7 @@ def overrides(method):
return wrapper
TIMEOUT = 60
TIMEOUT = 20
try:
TimeoutError = TimeoutError # @ReservedAssignment
@ -647,7 +647,7 @@ class DebuggerRunner(object):
except:
traceback.print_exc()
finish[0] = True
# print("Log on success: " + self.get_log_contents())
print("Log on success: " + self.get_log_contents())
def get_log_contents(self):
log_contents = ""
@ -1656,7 +1656,9 @@ class AbstractWriterThread(threading.Thread):
def wait_for_thread_join(self, main_thread_id):
def condition():
return self.get_frame_names(main_thread_id) in (
names = self.get_frame_names(main_thread_id)
return names in (
["join", "<module>"],
["wait", "join", "<module>"],
["_wait_for_tstate_lock", "join", "<module>"],
["_wait_for_tstate_lock", "join", "<module>", "_run_code", "_run_module_code", "run_path"],

Просмотреть файл

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>flask1</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

Просмотреть файл

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>my_django_proj_17</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
<nature>org.python.pydev.django.djangoNature</nature>
</natures>
</projectDescription>

Просмотреть файл

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
<key>DJANGO_MANAGE_LOCATION</key>
<value>manage.py</value>
</pydev_variables_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

Просмотреть файл

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>my_django_proj_21</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
<nature>org.python.pydev.django.djangoNature</nature>
</natures>
</projectDescription>

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
<key>DJANGO_MANAGE_LOCATION</key>
<value>manage.py</value>
<key>DJANGO_SETTINGS_MODULE</key>
<value>my_django_proj_21.settings</value>
</pydev_variables_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>
</pydev_pathproperty>
</pydev_project>

Просмотреть файл

@ -0,0 +1,27 @@
eclipse.preferences.version=1
encoding//.settings/org.python.pydev.yaml=UTF-8
encoding//_pydevd_bundle/_debug_adapter/pydevd_schema.py=utf-8
encoding//pydev_ipython/inputhook.py=utf-8
encoding//pydev_ipython/inputhookglut.py=utf-8
encoding//pydev_ipython/inputhookpyglet.py=utf-8
encoding//pydev_ipython/inputhookqt4.py=utf-8
encoding//pydev_ipython/inputhookqt5.py=utf-8
encoding//pydev_ipython/inputhookwx.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/breakpoint.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/crash.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/interactive.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/plugins/do_exploitable.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/process.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/__init__.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/defines.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/kernel32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/user32.py=utf-8
encoding//pydevd_attach_to_process/winappdbg/win32/version.py=utf-8
encoding//tests_python/debugger_fixtures.py=utf-8
encoding//tests_python/test_collect_bytecode_info.py=utf-8
encoding//tests_python/test_convert_utilities.py=utf-8
encoding//tests_python/test_debugger.py=utf-8
encoding//tests_python/test_debugger_json.py=utf-8
encoding//tests_python/test_extract_token.py=utf-8
encoding//tests_python/test_pydev_monkey.py=utf-8
encoding//tests_python/test_safe_repr.py=utf-8

Просмотреть файл

@ -0,0 +1,47 @@
ADD_NEW_LINE_AT_END_OF_FILE: true
AUTOPEP8_PARAMETERS: ''
AUTO_ADD_SELF: true
AUTO_BRACES: true
AUTO_COLON: true
AUTO_DEDENT_ELSE: true
AUTO_INDENT_AFTER_PAR_WIDTH: 1
AUTO_INDENT_TO_PAR_LEVEL: false
AUTO_LINK: false
AUTO_LITERALS: true
AUTO_PAR: true
AUTO_WRITE_IMPORT_STR: true
BLACK_PARAMETERS: ''
BLANK_LINES_INNER: 1
BLANK_LINES_TOP_LEVEL: 2
BREAK_IMPORTS_MODE: PARENTHESIS
DATE_FIELD_FORMAT: yyyy-MM-dd
DATE_FIELD_NAME: __updated__
DELETE_UNUSED_IMPORTS: false
ENABLE_DATE_FIELD_ACTION: false
FORMATTER_STYLE: PYDEVF
FORMAT_BEFORE_SAVING: true
FORMAT_ONLY_CHANGED_LINES: false
FORMAT_WITH_AUTOPEP8: false
FROM_IMPORTS_FIRST: false
GROUP_IMPORTS: true
IMPORT_ENGINE: IMPORT_ENGINE_PEP_8
INDENT_AFTER_PAR_AS_PEP8: false
MANAGE_BLANK_LINES: true
MULTILINE_IMPORTS: true
PEP8_IMPORTS: true
PYDEV_TEST_RUNNER: '2'
PYDEV_TEST_RUNNER_DEFAULT_PARAMETERS: --capture=no -vv --tb=native -n 0
PYDEV_USE_PYUNIT_VIEW: true
SAVE_ACTIONS_ONLY_ON_WORKSPACE_FILES: true
SMART_INDENT_PAR: true
SMART_LINE_MOVE: false
SORT_IMPORTS_ON_SAVE: false
SORT_NAMES_GROUPED: false
SPACES_BEFORE_COMMENT: '2'
SPACES_IN_START_COMMENT: '1'
TRIM_EMPTY_LINES: true
TRIM_MULTILINE_LITERALS: true
USE_ASSIGN_WITH_PACES_INSIDER_PARENTESIS: false
USE_OPERATORS_WITH_SPACE: true
USE_SPACE_AFTER_COMMA: true
USE_SPACE_FOR_PARENTESIS: false

Просмотреть файл

@ -1,38 +1,99 @@
import pydevd
import threading
import sys
original = pydevd.PyDB.notify_thread_created
found = set()
def new_notify_thread_created(self, thread_id, thread, *args, **kwargs):
found.add(thread)
return original(self, thread_id, thread, *args, **kwargs)
pydevd.PyDB.notify_thread_created = new_notify_thread_created
pydevd.PyDB.notify_thread_created = new_notify_thread_created
ok = []
class MyThread(threading.Thread):
def run(self):
if self not in found:
ok.append(False)
else:
ok.append(True)
if __name__ == '__main__':
threads = []
class ManualCreatedThreadPy313:
def __init__(self):
self.ev = threading.Event()
def run(self):
try:
if threading.current_thread() not in found:
ok.append(False)
else:
ok.append(True)
finally:
self.ev.set()
def start(self):
import _thread
_thread.start_joinable_thread(self.run)
def join(self):
self.ev.wait()
class ManualCreatedThreadFromThreadModule:
def __init__(self):
self.ev = threading.Event()
def run(self):
try:
if threading.current_thread() not in found:
ok.append(False)
else:
ok.append(True)
finally:
self.ev.set()
def start(self):
try:
import thread
except Exception:
import _thread as thread
thread.start_new_thread(self.run)
def join(self):
self.ev.wait()
if __name__ == "__main__":
threads: list = []
if sys.version_info[:2] >= (3, 13):
t1 = ManualCreatedThreadPy313()
t1.start()
threads.append(t1)
t2 = ManualCreatedThreadFromThreadModule()
t2.start()
threads.append(t2)
for i in range(15):
t = MyThread()
t.start()
threads.append(t)
for t in threads:
t.join()
assert len(ok) == len(threads)
assert all(ok), 'Expected all threads to be notified of their creation before starting to run. Found: %s' % (ok,)
assert all(ok), "Expected all threads to be notified of their creation before starting to run. Found: %s" % (ok,)
found.clear()
print('TEST SUCEEDED')
print("TEST SUCEEDED")

Просмотреть файл

@ -14,7 +14,8 @@ def method1():
method2() # handle on method1
except:
pass # Ok, handled
assert '__exception__' not in sys._getframe().f_locals
exc = sys._getframe().f_locals.get('__exception__', None)
assert exc is None
if __name__ == '__main__':

Просмотреть файл

@ -4,7 +4,7 @@ def Call():
variable_for_test_1 = 10 # Break here
variable_for_test_2 = 20
variable_for_test_3 = {'a':30, 'b':20}
locals()[u'\u16A0'] = u'\u16A1' # unicode variable (would be syntax error on py2).
= u'\u16A1'
all_vars_set = True # Break 2 here

Просмотреть файл

@ -0,0 +1,7 @@
import time
wait = True
while wait:
time.sleep(1) # break here
print('attached')

Просмотреть файл

@ -1,11 +1,6 @@
import time
import os
wait = True
while wait:
time.sleep(1) # break here
print('attached')
import _debugger_case_sysexit_unhandled_break
# Raise an exception in a system module.
def raise_exception():

Просмотреть файл

@ -2,38 +2,40 @@
# imported and having no other threads running).
def wait_for_condition(condition, msg=None, timeout=5, sleep=.05):
def wait_for_condition(condition, msg=None, timeout=5, sleep=0.05):
import time
curtime = time.time()
while True:
if condition():
break
if time.time() - curtime > timeout:
error_msg = 'Condition not reached in %s seconds' % (timeout,)
error_msg = "Condition not reached in %s seconds" % (timeout,)
if msg is not None:
error_msg += '\n'
error_msg += "\n"
if callable(msg):
error_msg += msg()
else:
error_msg += str(msg)
raise AssertionError('Timeout: %s' % (error_msg,))
raise AssertionError("Timeout: %s" % (error_msg,))
time.sleep(sleep)
def check_main_thread_id_simple():
import attach_script
import sys
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
try:
import thread
except ImportError:
import _thread as thread
main_thread_id, log_msg = attach_script.get_main_thread_id(None)
assert main_thread_id == thread.get_ident(), 'Found: %s, Expected: %s' % (main_thread_id, thread.get_ident())
assert main_thread_id == thread.get_ident(), "Found: %s, Expected: %s" % (main_thread_id, thread.get_ident())
assert not log_msg
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
wait_for_condition(lambda: len(sys._current_frames()) == 1)
@ -41,7 +43,8 @@ def check_main_thread_id_multiple_threads():
import attach_script
import sys
import time
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
try:
import thread
except ImportError:
@ -58,14 +61,14 @@ def check_main_thread_id_multiple_threads():
with lock:
thread.start_new_thread(method, ())
while not lock2.locked():
time.sleep(.1)
time.sleep(0.1)
wait_for_condition(lambda: len(sys._current_frames()) == 2)
main_thread_id, log_msg = attach_script.get_main_thread_id(None)
assert main_thread_id == thread.get_ident(), 'Found: %s, Expected: %s' % (main_thread_id, thread.get_ident())
assert main_thread_id == thread.get_ident(), "Found: %s, Expected: %s" % (main_thread_id, thread.get_ident())
assert not log_msg
assert 'threading' not in sys.modules
# assert 'threading' not in sys.modules
wait_for_condition(lambda: len(sys._current_frames()) == 1)
@ -73,7 +76,8 @@ def check_fix_main_thread_id_multiple_threads():
import attach_script
import sys
import time
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
try:
import thread
except ImportError:
@ -85,13 +89,25 @@ def check_fix_main_thread_id_multiple_threads():
def method():
lock2.acquire()
import threading # Note: imported on wrong thread
assert threading.current_thread().ident == thread.get_ident()
assert threading.current_thread() is attach_script.get_main_thread_instance(threading)
attach_script.fix_main_thread_id()
if sys.version_info[:2] >= (3, 13):
assert threading.current_thread().ident == thread.get_ident()
assert threading.current_thread().ident == thread.get_ident()
assert threading.current_thread() is not attach_script.get_main_thread_instance(threading)
# yay, Python 3.13 fixed this (so, no patchis is actually needed)
assert threading.current_thread() is not attach_script.get_main_thread_instance(threading)
# Call it just to make sure it doesn't raise any error.
attach_script.fix_main_thread_id()
assert threading.current_thread() is not attach_script.get_main_thread_instance(threading)
else:
assert threading.current_thread().ident == thread.get_ident()
assert threading.current_thread() is attach_script.get_main_thread_instance(threading)
attach_script.fix_main_thread_id()
assert threading.current_thread().ident == thread.get_ident()
assert threading.current_thread() is not attach_script.get_main_thread_instance(threading)
with lock:
pass # Will only finish when lock is released.
@ -99,27 +115,30 @@ def check_fix_main_thread_id_multiple_threads():
with lock:
thread.start_new_thread(method, ())
while not lock2.locked():
time.sleep(.1)
time.sleep(0.1)
wait_for_condition(lambda: len(sys._current_frames()) == 2)
wait_for_condition(lambda: len(sys._current_frames()) == 2, msg=(lambda: "Current frames: %s" % sys._current_frames()))
main_thread_id, log_msg = attach_script.get_main_thread_id(None)
assert main_thread_id == thread.get_ident(), 'Found: %s, Expected: %s' % (main_thread_id, thread.get_ident())
assert main_thread_id == thread.get_ident(), "Found: %s, Expected: %s" % (main_thread_id, thread.get_ident())
assert not log_msg
assert 'threading' in sys.modules
assert "threading" in sys.modules
import threading
assert threading.current_thread().ident == main_thread_id
wait_for_condition(lambda: len(sys._current_frames()) == 1)
def check_win_threads():
import sys
if sys.platform != 'win32':
if sys.platform != "win32":
return
import attach_script
import time
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
try:
import thread
except ImportError:
@ -141,18 +160,18 @@ def check_win_threads():
with lock:
windll.kernel32.CreateThread(None, c_size_t(0), method, None, c_uint32(0), None)
while not lock2.locked():
time.sleep(.1)
time.sleep(0.1)
wait_for_condition(lambda: len(sys._current_frames()) == 2)
main_thread_id, log_msg = attach_script.get_main_thread_id(None)
assert main_thread_id == thread.get_ident(), 'Found: %s, Expected: %s' % (main_thread_id, thread.get_ident())
assert main_thread_id == thread.get_ident(), "Found: %s, Expected: %s" % (main_thread_id, thread.get_ident())
assert not log_msg
assert 'threading' not in sys.modules
assert "threading" not in sys.modules
wait_for_condition(lambda: len(sys._current_frames()) == 1)
if __name__ == '__main__':
if __name__ == "__main__":
check_main_thread_id_simple()
check_main_thread_id_multiple_threads()
check_win_threads()

Просмотреть файл

@ -1,4 +0,0 @@
def exec_breakpoint():
# This exists so we can test that string frames from pydevd
# don't get handled
exec("breakpoint()")

Просмотреть файл

@ -1,12 +0,0 @@
if __name__ == '__main__':
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
# Create a breakpoint in a <string> frame
import _pydevd_string_breakpoint
_pydevd_string_breakpoint.exec_breakpoint()
# Now run the actual entry point
import empty_file
print('TEST SUCEEDED')

Просмотреть файл

@ -10,7 +10,6 @@ from tests_python.debugger_unittest import IS_CPYTHON, IS_PYPY
from _pydevd_bundle.pydevd_constants import IS_PY38_OR_GREATER, IS_JYTHON
from tests_python.debug_constants import IS_PY311_OR_GREATER, TODO_PYPY
def _method_call_with_error():
try:
_method_reraise()
@ -161,7 +160,7 @@ class _ExcVerifier(object):
if update_try_except_infos is not None:
update_try_except_infos(try_except_infos)
if sys.version_info[:2] not in ((3, 10), (3, 11), (3, 12)):
if sys.version_info[:2] not in ((3, 10), (3, 11), (3, 12), (3, 13)):
assert str(try_except_infos) == expected_as_str
from _pydevd_bundle.pydevd_collect_bytecode_info import collect_try_except_info_from_source
@ -193,7 +192,7 @@ def test_collect_try_except_info(data_regression, pyfile):
info = collect_try_except_info(method.__code__, use_func_first_line=True)
method_to_info[key] = sorted(str(x) for x in info)
if sys.version_info[:2] not in ((3, 10), (3, 11), (3, 12)):
if sys.version_info[:2] not in ((3, 10), (3, 11), (3, 12), (3, 13)):
data_regression.check(method_to_info)
data_regression.check(method_to_info_from_source)

Просмотреть файл

@ -1,11 +1,12 @@
# coding: utf-8
"""
The idea is that we record the commands sent to the debugger and reproduce them from this script
(so, this works as the client, which spawns the debugger as a separate process and communicates
to it as if it was run from the outside)
The idea is that we record the commands sent to the debugger and reproduce them from this script
(so, this works as the client, which spawns the debugger as a separate process and communicates
to it as if it was run from the outside)
Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python.
"""
import time
import pytest
@ -1523,7 +1524,7 @@ def test_case_handled_and_unhandled_exception_generator(case_setup, target_file,
if unhandled:
writer.write_add_exception_breakpoint_with_policy("Exception", "0", "1", "0")
else:
writer.write_add_exception_breakpoint_with_policy("Exception", "1", "0", "0")
writer.write_add_exception_breakpoint_with_policy("Exception", "1", "0", "1")
writer.write_make_initial_run()
@ -1726,7 +1727,7 @@ def test_unhandled_exceptions_in_top_level4(case_setup_unhandled_exceptions):
EXPECTED_RETURNCODE=1,
) as writer:
# Handled and unhandled
writer.write_add_exception_breakpoint_with_policy("Exception", "1", "1", "0")
writer.write_add_exception_breakpoint_with_policy("Exception", "1", "1", "1")
writer.write_make_initial_run()
# We have an exception thrown and handled and another which is thrown and is then unhandled.
@ -2258,7 +2259,7 @@ def test_redirect_output(case_setup):
if original_ignore_stderr_line(line):
return True
binary_junk = b"\xe8\xF0\x80\x80\x80"
binary_junk = b"\xe8\xf0\x80\x80\x80"
if sys.version_info[0] >= 3:
binary_junk = binary_junk.decode("utf-8", "replace")
@ -3044,7 +3045,9 @@ def test_attach_to_pid_no_threads(case_setup_remote, reattach):
writer.finished_ok = True
@pytest.mark.skipif(not IS_CPYTHON or IS_MAC or not SUPPORT_ATTACH_TO_PID or IS_PY312_OR_GREATER, reason="CPython only test (brittle on Mac).")
@pytest.mark.skipif(
not IS_CPYTHON or IS_MAC or not SUPPORT_ATTACH_TO_PID or IS_PY312_OR_GREATER, reason="CPython only test (brittle on Mac)."
)
def test_attach_to_pid_halted(case_setup_remote):
with case_setup_remote.test_file("_debugger_case_attach_to_pid_multiple_threads.py", wait_for_port=False) as writer:
time.sleep(1) # Give it some time to initialize and get to the proper halting condition

Просмотреть файл

@ -50,6 +50,7 @@ from _pydevd_bundle.pydevd_constants import (
PYDEVD_USE_SYS_MONITORING,
IS_PY312_OR_GREATER,
SUPPORT_ATTACH_TO_PID,
IS_PY313_OR_GREATER,
)
from tests_python import debugger_unittest
from tests_python.debug_constants import TEST_CHERRYPY, TEST_DJANGO, TEST_FLASK, IS_CPYTHON, TEST_GEVENT, TEST_CYTHON, IS_PY311
@ -1402,11 +1403,12 @@ def test_case_sys_exit_multiple_exception_attach(case_setup_remote, raised, unca
wait_for_condition(lambda: hasattr(writer, "reader_thread"))
json_facade = JsonFacade(writer)
json_facade.write_set_debugger_property([], ["_debugger_case_sysexit_unhandled_launcher.py"])
break_file = debugger_unittest._get_debugger_test_file("_debugger_case_sysexit_unhandled_break.py")
target_file = debugger_unittest._get_debugger_test_file("_debugger_case_sysexit_unhandled_attach.py")
bp_line = writer.get_line_index_with_content("break here")
final_line = writer.get_line_index_with_content("final break")
bp_line = writer.get_line_index_with_content("break here", filename=break_file)
handled_line = writer.get_line_index_with_content("@handled", filename=target_file)
unhandled_line = writer.get_line_index_with_content("@unhandled", filename=target_file)
original_ignore_stderr_line = writer._ignore_stderr_line
@ -1421,21 +1423,21 @@ def test_case_sys_exit_multiple_exception_attach(case_setup_remote, raised, unca
# Not really a launch, but we want to send these before the make_initial_run.
json_facade.write_launch(
breakpointOnSystemExit=True if zero else False,
debugOptions=["BreakOnSystemExitZero", "ShowReturnValue"] if zero else ["ShowReturnValue"],
breakpointOnSystemExit=True if zero else False,
debugOptions=["BreakOnSystemExitZero", "ShowReturnValue"] if zero else ["ShowReturnValue"],
)
json_facade.write_set_exception_breakpoints(filters)
json_facade.write_set_breakpoints([bp_line])
json_facade.write_set_breakpoints([bp_line], filename=break_file)
json_facade.write_make_initial_run()
hit = json_facade.wait_for_thread_stopped(line=bp_line)
hit = json_facade.wait_for_thread_stopped(line=bp_line, file=break_file)
# Stop looping
json_facade.get_global_var(hit.frame_id, "wait")
json_facade.write_set_variable(hit.frame_id, "wait", "False")
json_facade.write_set_breakpoints([])
json_facade.write_continue()
# When breaking on raised exceptions, we'll stop on both lines,
# unless it's SystemExit(0) and we asked to ignore that.
if raised and (zero or exit_code != 0):
@ -1451,12 +1453,6 @@ def test_case_sys_exit_multiple_exception_attach(case_setup_remote, raised, unca
)
json_facade.write_continue()
json_facade.wait_for_thread_stopped(
"exception",
line=final_line,
)
json_facade.write_continue()
# When breaking on uncaught exceptions, we'll stop on the second line,
# unless it's SystemExit(0) and we asked to ignore that.
# Note that if both raised and uncaught filters are set, there will be
@ -4227,7 +4223,7 @@ def test_wait_for_attach(case_setup_remote_attach_to_dap):
json_facade.write_list_threads()
# Check that we have the started thread event (whenever we reconnect).
started_events = json_facade.mark_messages(ThreadEvent, lambda x: x.body.reason == "started")
assert len(started_events) == 1
assert len(started_events) >= 1
def check_process_event(json_facade, start_method):
if start_method == "attach":
@ -5742,24 +5738,6 @@ def test_stop_on_entry2(case_setup_dap):
json_facade.write_continue()
writer.finished_ok = True
def test_stop_on_entry_verify_strings(case_setup_dap):
with case_setup_dap.test_file("not_my_code/main_on_entry3.py") as writer:
json_facade = JsonFacade(writer)
json_facade.write_set_debugger_property([], ["main_on_entry3.py", "_pydevd_string_breakpoint.py"])
json_facade.write_launch(
justMyCode=True,
stopOnEntry=True,
showReturnValue=True,
rules=[
{"path": "**/main_on_entry3.py", "include": False},
{"path": "**/_pydevd_string_breakpoint.py", "include": False},
],
)
json_facade.write_make_initial_run()
json_facade.wait_for_thread_stopped("breakpoint", file="empty_file.py")
json_facade.write_continue()
writer.finished_ok = True
@pytest.mark.parametrize("val", [True, False])
def test_debug_options(case_setup_dap, val):
@ -6257,11 +6235,14 @@ print('TEST SUCEEDED')
@pytest.mark.skipif(
not IS_WINDOWS
or not IS_PY36_OR_GREATER
or not IS_CPYTHON
or not TEST_CYTHON
or IS_PY311, # Requires frame-eval mode (not available for Python 3.11).
not (IS_PY312_OR_GREATER and IS_WINDOWS) # Always works with sys.monitoring (even without TEST_CYTHON)
and (
not IS_WINDOWS
or not IS_PY36_OR_GREATER
or not IS_CPYTHON
or not TEST_CYTHON
or IS_PY311 # Requires frame-eval mode (not available for Python 3.11).
),
# Note that this works in Python 3.12 as it uses sys.monitoring.
reason="Windows only test and only Python 3.6 onwards.",
)
@ -6300,6 +6281,41 @@ def test_native_threads(case_setup_dap, pyfile):
writer.finished_ok = True
@pytest.mark.skipif(not IS_PY313_OR_GREATER, reason="3.13 onwards only test.")
def test_internal_thread(case_setup_dap, pyfile):
@pyfile
def case_native_thread():
import _thread
import time
entered_thread = [False]
def method(*args, **kwargs):
entered_thread[0] = True # Break here
return 0
# Using it directly must still work!
_thread.start_joinable_thread(method)
while not entered_thread[0]:
time.sleep(0.1)
print("TEST SUCEEDED")
with case_setup_dap.test_file(case_native_thread) as writer:
json_facade = JsonFacade(writer)
line = writer.get_line_index_with_content("Break here")
json_facade.write_launch(justMyCode=False)
json_facade.write_set_breakpoints(line)
json_facade.write_make_initial_run()
json_facade.wait_for_thread_stopped(line=line)
json_facade.write_continue()
writer.finished_ok = True
def test_code_reload(case_setup_dap, pyfile):
@pyfile
def mod1():

Просмотреть файл

@ -1,4 +1,4 @@
from _pydevd_bundle.pydevd_constants import IS_PY38_OR_GREATER, NULL
from _pydevd_bundle.pydevd_constants import IS_PY313_OR_GREATER, IS_PY38_OR_GREATER, NULL, IS_PY313_0
from _pydevd_bundle.pydevd_xml import ExceptionOnEvaluate
import sys
@ -27,6 +27,7 @@ def disable_critical_log():
yield
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_basic(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -45,6 +46,7 @@ def test_evaluate_expression_basic(disable_critical_log):
assert "some_var" not in sys._getframe().f_globals
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_1(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -73,6 +75,7 @@ for s in container:
del sys._getframe().f_globals[varname]
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_2(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -84,6 +87,7 @@ def test_evaluate_expression_2(disable_critical_log):
check(global_frame)
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_3(disable_critical_log):
if not IS_PY38_OR_GREATER:
return
@ -104,6 +108,7 @@ def test_evaluate_expression_3(disable_critical_log):
assert "some_var" not in sys._getframe().f_globals
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_4(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -123,6 +128,7 @@ def test_evaluate_expression_4(disable_critical_log):
assert "email" not in sys._getframe().f_globals
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_access_globals(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -137,6 +143,7 @@ def test_evaluate_expression_access_globals(disable_critical_log):
assert "global_variable" not in sys._getframe().f_locals
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_create_none(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -149,6 +156,7 @@ def test_evaluate_expression_create_none(disable_critical_log):
check(next(iter(obtain_frame())))
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_delete_var(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -159,11 +167,15 @@ def test_evaluate_expression_delete_var(disable_critical_log):
eval_txt = "del x"
evaluate_expression(None, frame, eval_txt, is_exec=True)
assert "x" not in frame.f_locals
if IS_PY313_0:
assert frame.f_locals["x"] == None
else:
assert "x" not in frame.f_locals
check(next(iter(obtain_frame())))
@pytest.mark.skipif(IS_PY313_0, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_5(disable_critical_log):
from _pydevd_bundle.pydevd_vars import evaluate_expression
@ -243,7 +255,7 @@ def test_evaluate_expression_async_exec_as_eval(disable_critical_log):
asyncio.run(main())
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC, reason="Requires top-level async evaluation.")
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_exec_error(disable_critical_log):
py_db = _DummyPyDB()
@ -266,7 +278,7 @@ def test_evaluate_expression_async_exec_error(disable_critical_log):
asyncio.run(main())
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC, reason="Requires top-level async evaluation.")
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_eval(disable_critical_log):
py_db = _DummyPyDB()
@ -290,7 +302,7 @@ def test_evaluate_expression_async_eval(disable_critical_log):
asyncio.run(main())
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC, reason="Requires top-level async evaluation.")
@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_eval_error(disable_critical_log):
py_db = _DummyPyDB()

Просмотреть файл

@ -6,7 +6,7 @@ import time
from contextlib import contextmanager
from tests_python.debugger_unittest import IS_CPYTHON
from tests_python.debug_constants import TEST_CYTHON
from _pydevd_bundle.pydevd_constants import IS_PY312_OR_GREATER, IS_PY311_OR_GREATER
from _pydevd_bundle.pydevd_constants import IS_PY312_OR_GREATER, IS_PY311_OR_GREATER, IS_PY313_OR_GREATER, TODO_PY313_OR_GREATER
pytest_plugins = [
str("tests_python.debugger_fixtures"),
@ -296,6 +296,7 @@ def test_frame_eval_change_breakpoints(case_setup_force_frame_eval):
writer.finished_ok = True
@pytest.mark.skipif(TODO_PY313_OR_GREATER, reason="Flakey on python 3.13 or newer")
def test_generator_code_cache(case_setup_force_frame_eval):
with case_setup_force_frame_eval.test_file("_debugger_case_yield_from.py") as writer:
break1_line = writer.get_line_index_with_content("break1")

Просмотреть файл

@ -1,6 +1,6 @@
import sys
from _pydevd_bundle.pydevd_constants import IS_PY312_OR_GREATER, \
IS_PY311_OR_GREATER
IS_PY311_OR_GREATER, IS_PY313_OR_GREATER
try:
from _pydevd_bundle import pydevd_bytecode_utils
except ImportError:
@ -173,7 +173,9 @@ def test_smart_step_into_bytecode_info_002():
found = collect_smart_step_into_variants(
frame, 0, 99999, base=function.__code__.co_firstlineno)
if IS_PY311_OR_GREATER:
if IS_PY313_OR_GREATER:
check_name_and_line(found, [('sys._getframe()', 1), ('foo.bar(\n Something(param1, param2=xxx.yyy),\n )', 2), ('call()', 5)])
elif IS_PY311_OR_GREATER:
check_name_and_line(found, [('sys._getframe()', 1), ('foo.bar(\n Something(param1, param2=xxx.yyy),\n )', 2), ('Something(param1, param2=xxx.yyy)', 3), ('call()', 5)])
else:
check_name_and_line(found, [('_getframe', 1), ('bar', 2), ('Something', 3), ('call', 5)])
@ -194,7 +196,9 @@ def test_smart_step_into_bytecode_info_003():
found = collect_smart_step_into_variants(
frame, 0, 99999, base=function.__code__.co_firstlineno)
if IS_PY311_OR_GREATER:
if IS_PY313_OR_GREATER:
check_name_and_line(found, [('sys._getframe()', 1), ('foo.bar(\n Something(param1, param2=xxx.yyy), {}\n )', 2), ('call()', 5)])
elif IS_PY311_OR_GREATER:
check_name_and_line(found, [('sys._getframe()', 1), ('foo.bar(\n Something(param1, param2=xxx.yyy), {}\n )', 2), ('Something(param1, param2=xxx.yyy)', 3), ('call()', 5)])
else:
check_name_and_line(found, [('_getframe', 1), ('bar', 2), ('Something', 3), ('call', 5)])

233
subrepo.py Normal file
Просмотреть файл

@ -0,0 +1,233 @@
#!/usr/bin/env python
# pyright: strict
import argparse
import os
import shlex
import subprocess
import sys
from contextlib import contextmanager
from typing import Iterator
_SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
os.chdir(_SCRIPT_DIR)
_GIT_SUBREPO_ROOT = os.path.join(_SCRIPT_DIR, "build", "git-subrepo")
os.environ["GIT_SUBREPO_ROOT"] = _GIT_SUBREPO_ROOT
os.environ["PATH"] = (
os.path.join(_GIT_SUBREPO_ROOT, "lib") + os.pathsep + os.environ["PATH"]
)
os.environ["FILTER_BRANCH_SQUELCH_WARNING"] = "1"
_GIT_URL = "https://github.com/fabioz/PyDev.Debugger.git"
_SUBREPO_NAME = "src/debugpy/_vendored/pydevd"
_SUBREPO_TMP = ".git/tmp/subrepo/" + _SUBREPO_NAME
@contextmanager
def cwd(p: str) -> Iterator[None]:
old = os.getcwd()
os.chdir(p)
try:
yield
finally:
os.chdir(old)
def invoke_call(*args: str) -> None:
print(f"== {shlex.join(args)} ==")
subprocess.check_call(args)
def invoke_call_ok(*args: str) -> bool:
try:
subprocess.check_call(args)
return True
except:
return False
def invoke_output(*args: str, no_log: bool = False) -> str:
if not no_log:
print(f"== {shlex.join(args)} ==")
return subprocess.check_output(args, text=True)
def get_current_commit() -> str:
return invoke_output(
"git",
"config",
"--file",
f"{_SUBREPO_NAME}/.gitrepo",
"subrepo.commit",
no_log=True,
).strip()
def err_exit(message: str):
print(message, file=sys.stderr)
sys.exit(1)
def clone() -> None:
# Clone the repo.
invoke_call("git", "subrepo", "clone", _GIT_URL, _SUBREPO_NAME)
def reclone() -> None:
# Remove the temporary branch and worktree.
invoke_call("git", "subrepo", "clean", _SUBREPO_NAME)
# Force clone the repo.
invoke_call("git", "subrepo", "clone", "--force", _GIT_URL, _SUBREPO_NAME)
def pull() -> None:
# Remove the temporary branch and worktree.
invoke_call("git", "subrepo", "clean", "--force", _SUBREPO_NAME)
invoke_call("git", "subrepo", "fetch", _SUBREPO_NAME)
with cwd(_SUBREPO_NAME):
new_commit = invoke_output("git", "rev-parse","--verify", "FETCH_HEAD").strip()
print(f"Updating to pydevd commit {new_commit}.")
# Pull changes and squash commit them.
invoke_call("git", "subrepo", "pull", _SUBREPO_NAME)
# Now, branch to see if there are any diffs. If not, then we can just reclone.
print("Branching to check if the pydevd tree is clean.")
invoke_call("git", "subrepo", "clean", _SUBREPO_NAME)
invoke_call("git", "subrepo", "branch", _SUBREPO_NAME)
with cwd(_SUBREPO_TMP):
no_diff = invoke_call_ok("git", "diff", "--quiet", new_commit)
if no_diff:
# No diff, so it's safe to manually move the subrepo parent to HEAD.
print("pydevd tree is clean, moving subrepo parent.")
new_parent = invoke_output("git", "rev-parse", "HEAD", no_log=True).strip()
invoke_call(
"git",
"config",
"--file",
"src/debugpy/_vendored/pydevd/.gitrepo",
"subrepo.parent",
new_parent,
)
invoke_call("git", "commit", "-am", "Update git-subrepo parent")
else:
print("pydevd tree has changes not pushed upstream.")
def branch(message: str) -> None:
current_commit = get_current_commit()
# Remove the temporary branch and worktree.
invoke_call("git", "subrepo", "clean", _SUBREPO_NAME)
# Ensure we have all of the subrepo refs.
invoke_call("git", "subrepo", "fetch", _SUBREPO_NAME)
# Populate the subrepo/src/debugpy/_vendored/pydevd branch with new changes.
invoke_call("git", "subrepo", "branch", _SUBREPO_NAME)
# Enter worktree; changes here are applied to the subrepo/src/debugpy/_vendored/pydevd branch.
with cwd(_SUBREPO_TMP):
# Remove all commits after the last pull and restage the difference.
invoke_call("git", "reset", "--soft", current_commit)
hasModifiedFile = invoke_output("git", "status","-s").strip()
if hasModifiedFile:
# Commit changes with a new message.
invoke_call("git", "commit", "-m", message)
def push_to_fork(fork_remote: str, fork_branch: str) -> None:
invoke_call("git", "push", fork_remote, f"subrepo/src/debugpy/_vendored/pydevd:{fork_branch}")
def commit() -> None:
invoke_call("git", "subrepo", "commit", _SUBREPO_NAME)
def main() -> None:
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
subparsers = parser.add_subparsers(dest="subcommand", required=True)
subparsers.add_parser(
"clone",
help="clones pydevd for the first time",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
subparsers.add_parser(
"reclone",
help="force reclones pydevd",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
subparsers.add_parser(
"pull",
help="pulls pydevd",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
branch_parser = subparsers.add_parser(
"branch",
help="squashes changes to pydevd",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
branch_parser.add_argument(
"-m",
"--message",
dest="message",
required=True,
help="message for the squashed commit",
)
ptf_parser = subparsers.add_parser(
"push-to-fork",
help="pushes the squashed changes to pydevd",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
ptf_parser.add_argument(
"--fork-remote",
dest="fork_remote",
default="pydevd-fork",
help="pydevd remote",
)
ptf_parser.add_argument(
"--fork-branch",
dest="fork_branch",
required=True,
help="branch to push to on the remote",
)
subparsers.add_parser(
"commit",
help=f"runs 'git subrepo commit {_SUBREPO_NAME}'",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
args = parser.parse_args()
if args.subcommand == "clone":
clone()
elif args.subcommand == "reclone":
reclone()
elif args.subcommand == "pull":
pull()
elif args.subcommand == "branch":
branch(args.message)
elif args.subcommand == "push-to-fork":
push_to_fork(args.fork_remote, args.fork_branch)
elif args.subcommand == "commit":
commit()
else:
parser.print_help()
if __name__ == "__main__":
main()