|
| 1 | +#🐞 Setting Up Backend Debugger in VS Code for Specify 7 |
| 2 | +This guide walks you through configuring VS Code to enable Django backend debugging in the Specify 7 codebase. |
| 3 | + |
| 4 | +✅ **Prerequisites** |
| 5 | +- VS Code installed |
| 6 | +- Docker and Docker Compose installed |
| 7 | +- Python extension installed in VS Code |
| 8 | +- Your development environment is cloned and running properly |
| 9 | + |
| 10 | +🧰 ### 1. **VS Code Debug Configuration** |
| 11 | +- Open VS Code. |
| 12 | +- Click on the Run and Debug icon in the sidebar. |
| 13 | +- Click "Create a launch.json file". |
| 14 | +- Select Python. |
| 15 | +- Choose Django. |
| 16 | +- Choose manage.py. |
| 17 | +- Replace the content of .vscode/launch.json with: |
| 18 | + |
| 19 | +```json |
| 20 | +{ |
| 21 | + "version": "0.2.0", |
| 22 | + "configurations": [ |
| 23 | + { |
| 24 | + "name": "Run Django", |
| 25 | + "type": "python", |
| 26 | + "request": "attach", |
| 27 | + "pathMappings": [ |
| 28 | + { |
| 29 | + "localRoot": "${workspaceFolder}/specifyweb", |
| 30 | + "remoteRoot": "/opt/specify7/specifyweb" |
| 31 | + } |
| 32 | + ], |
| 33 | + "port": 3000, |
| 34 | + "host": "127.0.0.1" |
| 35 | + } |
| 36 | + ] |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | + |
| 41 | +🛠 ### 2. **Required Code & Config Changes** |
| 42 | +📁 Add .vscode/settings.json: |
| 43 | +```json |
| 44 | +[ |
| 45 | + { |
| 46 | + "python.testing.unittestArgs": [ |
| 47 | + "-v", |
| 48 | + "-s", |
| 49 | + "./specifyweb", |
| 50 | + "-p", |
| 51 | + "*test*.py" |
| 52 | + ], |
| 53 | + "python.testing.pytestEnabled": false, |
| 54 | + "python.testing.unittestEnabled": true, |
| 55 | + "python.linting.mypyEnabled": true, |
| 56 | + "python.linting.enabled": true, |
| 57 | + "typescript.tsdk": "./specifyweb/frontend/js_src/node_modules/typescript/lib" |
| 58 | + }, |
| 59 | + { |
| 60 | + "typescript.tsdk": "./specifyweb/frontend/js_src/node_modules/typescript/lib", |
| 61 | + "eslint.experimental.useFlatConfig": true, |
| 62 | + "eslint.workingDirectories": [ |
| 63 | + { "pattern": "./specifyweb/frontend/js_src/" } |
| 64 | + ] |
| 65 | + } |
| 66 | +] |
| 67 | +``` |
| 68 | + |
| 69 | +⚙️ **Modify manage.py:** |
| 70 | +Insert this before execute_from_command_line(sys.argv): |
| 71 | +```python |
| 72 | +if settings.DEBUG: |
| 73 | + if os.environ.get('RUN_MAIN') or os.environ.get('WERKZEUG_RUN_MAIN'): |
| 74 | + import debugpy |
| 75 | + debugpy.listen(("0.0.0.0", 3000)) |
| 76 | + print('Attached!') |
| 77 | +``` |
| 78 | + |
| 79 | +🐳 **In docker-compose.yml, under specify7 service:** |
| 80 | +Add the line under commands: |
| 81 | +```yaml |
| 82 | +specify7: |
| 83 | + ... |
| 84 | + ports: |
| 85 | + - "3000:3000" |
| 86 | +``` |
| 87 | +
|
| 88 | +🐳 **Update Dockerfile** |
| 89 | +Use the new Dockerfile provided in this documentation. |
| 90 | +
|
| 91 | +📦 **Add debugpy to requirements-testing.txt:** |
| 92 | +```txt |
| 93 | +debugpy==1.6.5 |
| 94 | +django-stubs==1.12.0 |
| 95 | +mypy==0.971 |
| 96 | +hypothesis==6.4.0 |
| 97 | +hypothesis-jsonschema==0.19.0 |
| 98 | +types-requests==2.28.5 |
| 99 | +pytest==7.2.1 |
| 100 | +pytest-django==4.5.2 |
| 101 | +``` |
| 102 | + |
| 103 | +🧪 **First-Time Setup Steps** |
| 104 | +Stop any running Docker containers: |
| 105 | +```bash |
| 106 | +docker compose down |
| 107 | +``` |
| 108 | + |
| 109 | +Rebuild the image (for first-time or after Dockerfile changes): |
| 110 | +```bash |
| 111 | +docker compose build |
| 112 | +``` |
| 113 | + |
| 114 | +Start your development server:
|
| 115 | +```bash |
| 116 | +docker compose up |
| 117 | +``` |
| 118 | + |
| 119 | +🐞 ### 3. **Running the Debugger** |
| 120 | +- Put breakpoints in your Django code. |
| 121 | +- In VS Code, open the Run and Debug panel. |
| 122 | +- Select "Run Django" and press the ▶️ Play icon. |
| 123 | +- Open the app in your browser as usual (e.g. http://localhost:8000) and trigger code paths to hit breakpoints. |
| 124 | + |
| 125 | +🔁 **When Changing Code** |
| 126 | +To reload the debugger: |
| 127 | +- Click the disconnect icon (next to Play) in the Debug bar. |
| 128 | +- Click Play again to reconnect. |
| 129 | + |
| 130 | +🪟 **Windows-Specific Notes** |
| 131 | +If you're running on WSL / Ubuntu, modify the Dockerfile: |
| 132 | +Replace: |
| 133 | +```yaml |
| 134 | +FROM arm64v8/… |
| 135 | +``` |
| 136 | + |
| 137 | +With: |
| 138 | +Dockerfile |
| 139 | + |
| 140 | +```yaml |
| 141 | +FROM ubuntu:24.04 |
| 142 | +FROM node:18-alpine |
| 143 | +``` |
| 144 | +New dockerfile: |
| 145 | +```bash |
| 146 | +# FROM ubuntu:18.04 AS common |
| 147 | +FROM arm64v8/ubuntu:18.04 AS common |
| 148 | + |
| 149 | +LABEL maintainer="Specify Collections Consortium <github.com/specify>" |
| 150 | + |
| 151 | +RUN apt-get update \ |
| 152 | + && apt-get -y install --no-install-recommends \ |
| 153 | + gettext \ |
| 154 | + python3.8 \ |
| 155 | + libldap-2.4-2 \ |
| 156 | + libmariadbclient18 \ |
| 157 | + sudo \ |
| 158 | + && apt-get clean \ |
| 159 | + && rm -rf /var/lib/apt/lists/* |
| 160 | + |
| 161 | +RUN groupadd -g 999 specify \ |
| 162 | + && useradd -r -u 999 -g specify specify |
| 163 | + |
| 164 | +RUN mkdir -p /home/specify \ |
| 165 | + && chown specify.specify /home/specify |
| 166 | +RUN mkdir -p /opt/specify7 \ |
| 167 | + && chown specify.specify /opt/specify7 |
| 168 | + |
| 169 | + |
| 170 | +##################################################################### |
| 171 | + |
| 172 | + |
| 173 | +# FROM node:18-alpine AS build-frontend |
| 174 | +FROM arm64v8/node:18-alpine AS build-frontend |
| 175 | + |
| 176 | +LABEL maintainer="Specify Collections Consortium <github.com/specify>" |
| 177 | + |
| 178 | +USER node |
| 179 | +WORKDIR /home/node |
| 180 | + |
| 181 | +COPY --chown=node:node specifyweb/frontend/js_src/package*.json ./ |
| 182 | +RUN npm ci |
| 183 | +RUN mkdir dist && chown node:node dist |
| 184 | +COPY --chown=node:node specifyweb/frontend/js_src . |
| 185 | +RUN npx webpack --mode production |
| 186 | + |
| 187 | + |
| 188 | +##################################################################### |
| 189 | + |
| 190 | +FROM common AS build-backend |
| 191 | + |
| 192 | +RUN apt-get update \ |
| 193 | + && apt-get -y install --no-install-recommends \ |
| 194 | + build-essential \ |
| 195 | + ca-certificates \ |
| 196 | + curl \ |
| 197 | + git \ |
| 198 | + sudo \ |
| 199 | + libldap2-dev \ |
| 200 | + libmariadbclient-dev \ |
| 201 | + libsasl2-dev \ |
| 202 | + python3.8-venv \ |
| 203 | + python3.8-distutils \ |
| 204 | + python3.8-dev |
| 205 | + |
| 206 | +USER specify |
| 207 | +COPY --chown=specify:specify requirements.txt /home/specify/ |
| 208 | + |
| 209 | +WORKDIR /opt/specify7 |
| 210 | +# RUN python3.8 -m venv ve \ |
| 211 | +# && ve/bin/pip install --no-cache-dir -r /home/specify/requirements.txt |
| 212 | +RUN python3.8 -m venv ve |
| 213 | +# RUN ve/bin/pip install --no-cache-dir backports.zoneinfo[tzdata]>=0.2.1 |
| 214 | +RUN ve/bin/pip install --no-cache-dir -r /home/specify/requirements.txt |
| 215 | +RUN ve/bin/pip install --no-cache-dir gunicorn |
| 216 | + |
| 217 | +COPY --from=build-frontend /home/node/dist specifyweb/frontend/static/js |
| 218 | +COPY --chown=specify:specify specifyweb /opt/specify7/specifyweb |
| 219 | +COPY --chown=specify:specify manage.py /opt/specify7/ |
| 220 | +COPY --chown=specify:specify docker-entrypoint.sh /opt/specify7/ |
| 221 | +COPY --chown=specify:specify Makefile /opt/specify7/ |
| 222 | +COPY --chown=specify:specify specifyweb.wsgi /opt/specify7/ |
| 223 | + |
| 224 | +ARG BUILD_VERSION |
| 225 | +ARG GIT_SHA |
| 226 | +ENV BUILD_VERSION=$BUILD_VERSION |
| 227 | +RUN make specifyweb/settings/build_version.py |
| 228 | +RUN echo $BUILD_VERSION > specifyweb/frontend/static/build_version.txt |
| 229 | +RUN echo $GIT_SHA > specifyweb/frontend/static/git_sha.txt |
| 230 | +RUN date > specifyweb/frontend/static/build_date.txt |
| 231 | + |
| 232 | +# The following is needed to run manage.py compilemessages: |
| 233 | +# The secret key file needs to exist so it can be imported. |
| 234 | +# The INSTALLED_APPS needs to be cleared out so Django doesn't |
| 235 | +# try to import the Specify datamodel which isn't defined yet. |
| 236 | +RUN echo "SECRET_KEY = 'bogus'" > specifyweb/settings/secret_key.py |
| 237 | +RUN echo "INSTALLED_APPS = ['specifyweb.frontend']" >> specifyweb/settings/__init__.py |
| 238 | +# RUN (cd specifyweb && ../ve/bin/python ../manage.py compilemessages) |
| 239 | +RUN ve/bin/python manage.py compilemessages |
| 240 | + |
| 241 | +# Now put things back the way they were. |
| 242 | +RUN rm specifyweb/settings/secret_key.py |
| 243 | +COPY --chown=specify:specify specifyweb/settings/__init__.py /opt/specify7/specifyweb/settings/__init__.py |
| 244 | + |
| 245 | +###################################################################### |
| 246 | + |
| 247 | +FROM common AS run-common |
| 248 | + |
| 249 | +RUN apt-get update \ |
| 250 | + && apt-get -y install --no-install-recommends \ |
| 251 | + rsync \ |
| 252 | + && apt-get clean \ |
| 253 | + && rm -rf /var/lib/apt/lists/* |
| 254 | + |
| 255 | +RUN mkdir -p /volumes/static-files/depository \ |
| 256 | + && chown -R specify.specify /volumes/static-files |
| 257 | + |
| 258 | +USER specify |
| 259 | +COPY --from=build-backend /opt/specify7 /opt/specify7 |
| 260 | + |
| 261 | +WORKDIR /opt/specify7 |
| 262 | +RUN cp -r specifyweb/settings . |
| 263 | + |
| 264 | +RUN echo \ |
| 265 | + "import os" \ |
| 266 | + "\nDATABASE_NAME = os.environ['DATABASE_NAME']" \ |
| 267 | + "\nDATABASE_HOST = os.environ['DATABASE_HOST']" \ |
| 268 | + "\nDATABASE_PORT = os.environ.get('DATABASE_PORT', '')" \ |
| 269 | + "\nMASTER_NAME = os.environ['MASTER_NAME']" \ |
| 270 | + "\nMASTER_PASSWORD = os.environ['MASTER_PASSWORD']" \ |
| 271 | + "\nDEPOSITORY_DIR = '/volumes/static-files/depository'" \ |
| 272 | + "\nREPORT_RUNNER_HOST = os.getenv('REPORT_RUNNER_HOST', '')" \ |
| 273 | + "\nREPORT_RUNNER_PORT = os.getenv('REPORT_RUNNER_PORT', '')" \ |
| 274 | + "\nWEB_ATTACHMENT_URL = os.getenv('ASSET_SERVER_URL', None)" \ |
| 275 | + "\nWEB_ATTACHMENT_KEY = os.getenv('ASSET_SERVER_KEY', None)" \ |
| 276 | + "\nWEB_ATTACHMENT_COLLECTION = os.getenv('ASSET_SERVER_COLLECTION', None)" \ |
| 277 | + "\nSEPARATE_WEB_ATTACHMENT_FOLDERS = os.getenv('SEPARATE_WEB_ATTACHMENT_FOLDERS', None)" \ |
| 278 | + "\nCELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', None)" \ |
| 279 | + "\nCELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', None)" \ |
| 280 | + "\nCELERY_TASK_DEFAULT_QUEUE = os.getenv('CELERY_TASK_QUEUE', DATABASE_NAME)" \ |
| 281 | + "\nANONYMOUS_USER = os.getenv('ANONYMOUS_USER', None)" \ |
| 282 | + > settings/local_specify_settings.py |
| 283 | + |
| 284 | +RUN echo "import os \nDEBUG = os.getenv('SP7_DEBUG', '').lower() == 'true'\n" \ |
| 285 | + > settings/debug.py |
| 286 | + |
| 287 | +RUN echo "import os \nSECRET_KEY = os.environ['SECRET_KEY']\n" \ |
| 288 | + > settings/secret_key.py |
| 289 | + |
| 290 | +ENV LC_ALL=C.UTF-8 |
| 291 | +ENV LANG=C.UTF-8 |
| 292 | +ENV DJANGO_SETTINGS_MODULE='settings' |
| 293 | + |
| 294 | +ENTRYPOINT ["/opt/specify7/docker-entrypoint.sh"] |
| 295 | + |
| 296 | +EXPOSE 8000 |
| 297 | + |
| 298 | + |
| 299 | +###################################################################### |
| 300 | + |
| 301 | +FROM run-common AS run-development |
| 302 | + |
| 303 | +USER root |
| 304 | + |
| 305 | +RUN apt-get update \ |
| 306 | + && apt-get -y install --no-install-recommends \ |
| 307 | + python3.8-distutils \ |
| 308 | + ca-certificates \ |
| 309 | + make |
| 310 | + |
| 311 | +USER specify |
| 312 | + |
| 313 | +COPY requirements-testing.txt /home/specify/ |
| 314 | + |
| 315 | +COPY --chown=specify:specify requirements-testing.txt /home/specify/ |
| 316 | +COPY --chown=specify:specify requirements.txt /home/specify/ |
| 317 | + |
| 318 | +COPY --chown=specify:specify .vscode/launch.json /opt/specify/ |
| 319 | +COPY --chown=specify:specify .vscode/settings.json /opt/specify/ |
| 320 | + |
| 321 | +RUN ve/bin/pip install --no-cache-dir -r /home/specify/requirements-testing.txt |
| 322 | +RUN ve/bin/pip install --no-cache-dir -r /home/specify/requirements.txt |
| 323 | + |
| 324 | +# RUN python3.8 -m venv ve \ |
| 325 | +# && ve/bin/pip install --no-cache-dir -r /home/specify/requirements-testing.txt \ |
| 326 | +# && ve/bin/pip install --no-cache-dir -r /home/specify/requirements.txt |
| 327 | + |
| 328 | +# RUN mkdir /opt/specify7/.vscode |
| 329 | +# RUN echo "[pytest]\nDJANGO_SETTINGS_MODULE=specifyweb.settings\npython_files=*test*.py testparsing.py\naddopts = --ignore=specifyweb/specify/selenium_tests.py" > /opt/specify7/specifyweb/pytest.ini |
| 330 | +# RUN echo "{\n\t\"python.pythonPath\": \"ve/bin/python/\",\n\t\"python.testing.pytestArgs\": [\n\t\t\"specifyweb\",\n\t\t\"-s\",\n\t\t\"-vv\"\n\t],\n\t\"python.testing.pytestEnabled\": true,\n\t\"python.testing.nosetestsEnabled\": false,\n\t\"python.testing.unittestEnabled\": false\n}" > /opt/specify7/.vscode/settings.json |
| 331 | + |
| 332 | +# COPY mypy.ini ./ |
| 333 | + |
| 334 | + |
| 335 | +###################################################################### |
| 336 | + |
| 337 | +FROM run-common AS run |
| 338 | + |
| 339 | +RUN mv specifyweb.wsgi specifyweb_wsgi.py |
| 340 | + |
| 341 | +CMD ["ve/bin/gunicorn", "-w", "3", "-b", "0.0.0.0:8000", "-t", "300", "specifyweb_wsgi"] |
| 342 | + |
| 343 | + |
| 344 | +``` |
| 345 | + |
| 346 | + |
0 commit comments