1"use strict";(self.webpackChunk_ab_docs_main_app=self.webpackChunk_ab_docs_main_app||[]).push([["18522"],{631353:function(e,n,i){i.r(n),i.d(n,{assets:()=>c,contentTitle:()=>o,default:()=>h,frontMatter:()=>d,metadata:()=>s,toc:()=>l});var s=i(314537),t=i(552322),r=i(822840);let d={id:"debugging-guide-service-extension-python",title:"Local debugging guide \u2014 Python",description:"Step-by-step guide to debugging an Extend Service Extension app written in Python, covering environment setup, debugpy attachment, log reading, and common issue diagnosis.",sidebar_label:"Python",ab_notes:"",ab_teams:"ab_team_extend"},o=void 0,c={},l=[{value:"Project structure",id:"project-structure",level:2},{value:"Running the service locally",id:"running-the-service-locally",level:2},{value:"From the terminal",id:"from-the-terminal",level:3},{value:"From VS Code",id:"from-vs-code",level:3},{value:"Confirming the service is up",id:"confirming-the-service-is-up",level:3},{value:"Attaching the debugger",id:"attaching-the-debugger",level:2},{value:"VS Code (recommended)",id:"vs-code-recommended",level:3},{value:"Other IDEs \u2014 remote debugpy attach",id:"other-ides--remote-debugpy-attach",level:3},{value:"Where to put breakpoints",id:"where-to-put-breakpoints",level:2},{value:"Reading logs",id:"reading-logs",level:2},{value:"Testing endpoints manually",id:"testing-endpoints-manually",level:2},{value:"Python-specific troubleshooting",id:"python-specific-troubleshooting",level:2},{value:"Proto changes have no effect",id:"proto-changes-have-no-effect",level:3},{value:"<code>ModuleNotFoundError</code> for <code>app</code>",id:"modulenotfounderror-for-app",level:3},{value:"<code>debugpy</code> not found",id:"debugpy-not-found",level:3},{value:"Checking for port conflicts",id:"checking-for-port-conflicts",level:3},{value:"AI assistance",id:"ai-assistance",level:2},{value:"References",id:"references",level:2}];function a(e){let n={a:"a",code:"code",h2:"h2",h3:"h3",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,r.a)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["This guide covers everything specific to debugging an Extend Service Extension app written in\n",(0,t.jsx)(n.strong,{children:"Python"}),". For general debugging concepts \u2014 environment setup, VS Code debug workflow, log\nreading, and common issues \u2014 see:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/",children:"Extend local debugging guide"}
1)," \u2014 common concepts for all Extend app types"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/local-debugging-python",children:"Python language setup guide"})," \u2014 Python prerequisites, debugpy, jq, and Python-specific troubleshooting"]}),"\n"]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"project-structure",children:"Project structure"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"src/\n\u2514\u2500\u2500 app/\n \u251C\u2500\u2500 __main__.py # Entry point\n \u251C\u2500\u2500 services/\n \u2502 \u2514\u2500\u2500 my_service.py # Your business logic\n \u2514\u2500\u2500 ...\nproto/\n\u2514\u2500\u2500 app/\n \u2514\u2500\u2500 service.proto # gRPC API definition\n"})}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"File"}),(0,t.jsx)(n.th,{children:"What it does"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"src/app/__main__.py"})}),(0,t.jsx)(n.td,{children:"Entry point \u2014 starts the gRPC server and gRPC-Gateway."})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"src/app/services/my_service.py"})}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.strong,{children:"Your business logic"})," \u2014 implements the gRPC service methods."]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"src/accelbyte_grpc_plugin/interceptors/authorization.py"})}),(0,t.jsx)(n.td,{children:"Validates every incoming request's IAM token and permission."})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"proto/app/service.proto"})}),(0,t.jsx)(n.td,{children:"Defines the gRPC API \u2014 endpoints, request/response shapes, and required permissions."})]})]})]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Port numbers"}),":"]}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Port"}),(0,t.jsx)(n.th,{children:"Purpose"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"6565"})}),(0,t.jsx)(n.td,{children:"gRPC server (internal \u2014 used by gRPC-Gateway)"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"8000"})}),(0,t.jsxs)(n.td,{children:["gRPC-Gateway HTTP/REST \u2014 call this from a browser, Postman, or ",(0,t.jsx)(n.code,{children:"curl"})]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"8080"})}),(0,t.jsxs)(n.td,{children:["Prometheus metrics endpoint (",(0,t.jsx)(n.code,{children:"/metrics"}),")"]})]})]})]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"running-the-service-locally",children:"Running the service locally"}),"\n",(0,t.jsx)(n.h3,{id:"from-the-terminal",children:"From the terminal"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"# Export all variables from your .env file\nexport $(grep -v '^#' .env | xargs)\n\npython -m app\n"})}),"\n",(0,t.jsx)(n.h3,{id:"from-vs-code",children:"From VS Code"}),"\n",(0,t.jsxs)(n.p,{children:["Use ",(0,t.jsx)(n.strong,{children:'Terminal \u2192 Run Task \u2192 "Run: Service"'}),".\nThis task is defined in ",(0,t.jsx)(n.code,{children:".vscode/tasks.json"})," and loads the ",(0,t.jsx)(n.code,{children:".env"})," file for you automatically."]}),"\n",(0,t.jsx)(n.h3,{id:"confirming-the-service-is-up",children:"Confirming the service is up"}),"\n",(0,t.jsx)(n.p,{children:"You should see logs like:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{"time":"...","level":"INFO","msg":"app server started","service":"extend-app-service-extension"}\n{"time":"...","level":"INFO","msg":"starting gRPC-Gateway HTTP server","port":8000}\n{"time":"...","level":"INFO","msg":"serving prometheus metrics","port":8080,"endpoint":"/metrics"}\n'})}),"\n",(0,t.jsxs)(n.p,{children:["Open ",(0,t.jsx)(n.code,{children:"http://localhost:8000<BASE_PATH>/apidocs/"})," in your browser to verify Swagger UI is live."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"attaching-the-debugger",children:"Attaching the debugger"}),"\n",(0,t.jsx)(n.h3,{id:"vs-code-recommended",children:"VS Code (recommended)"}),"\n",(0,t.jsxs)(n.p,{children:["The repository ships with a ready-to-use launch configuration in ",(0,t.jsx)(n.code,{children:".vscode/launch.json"})," that\nuses ",(0,t.jsx)(n.code,{children:"debugpy"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-jsonc",children:'{\n "name": "Debug: Service",\n "type": "python",\n "request": "launch",\n "module": "app",\n "cwd": "${workspaceFolder}/src",\n "envFile": "${workspaceFolder}/.env"\n}\n'})}),"\n",(0,t.jsxs)(n.p,{children:["Follow the ",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/#attaching-the-debugger-in-vs-code",children:"attaching the debugger"}),"\nsteps in the generic guide, then select ",(0,t.jsx)(n.strong,{children:'"Debug: Service"'})," from the dropdown."]}),"\n",(0,t.jsx)(n.h3,{id:"other-ides--remote-debugpy-attach",children:"Other IDEs \u2014 remote debugpy attach"}),"\n",(0,t.jsxs)(n.p,{children:["Start the app with ",(0,t.jsx)(n.code,{children:"debugpy"})," listening on port ",(0,t.jsx)(n.code,{children:"5678"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"export $(grep -v '^#' .env | xargs)\npython -m debugpy --listen 5678 --wait-for-client -m app\n"})}),"\n",(0,t.jsx)(n.p,{children:"The process pauses until a debugger connects. Attach from PyCharm, a remote debugger, or a\nsecond VS Code window using:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-jsonc",children:'{\n "name": "Attach: Remote",\n "type": "python",\n "request": "attach",\n "connect": { "host": "localhost", "port": 5678 }\n}\n'})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"where-to-put-breakpoints",children:"Where to put breakpoints"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"What you want to investigate"}),(0,t.jsx)(n.th,{children:"File and location"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"A specific REST endpoint being called"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.code,{children:"services/my_service.py"})," \u2014 top of the relevant method"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Auth/token validation failure"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.code,{children:"accelbyte_grpc_plugin/interceptors/authorization.py"})," \u2014 the request interception point"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Data not saving or loading correctly"}),(0,t.jsx)(n.td,{children:"The storage or repository module used in your service"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:"Service not starting at all"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.code,{children:"app/__main__.py"})," \u2014 the server setup block"]})]})]})]}),"\n",(0,t.jsxs)(n.p,{children:["For conditional breakpoint syntax, see the\n",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/local-debugging-python#conditional-breakpoint-syntax",children:"Python language guide"}),"."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"reading-logs",children:"Reading logs"}),"\n",(0,t.jsxs)(n.p,{children:["For ",(0,t.jsx)(n.code,{children:"jq"})," log filtering, see the\n",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/local-debugging-python#reading-logs-with-jq",children:"Python language guide"}),"."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"testing-endpoints-manually",children:"Testing endpoints manually"}),"\n",(0,t.jsxs)(n.p,{children:["For ",(0,t.jsx)(n.code,{children:"curl"})," and ",(0,t.jsx)(n.code,{children:"grpcurl"})," usage, see\n",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/#testing-with-grpcurl",children:"Testing with grpcurl"})," in the generic guide."]}),"\n",(0,t.jsxs)(n.p,{children:["For Swagger UI testing (SE-specific), see\n",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/service-extension/debugging-guide/#testing-endpoints-manually",children:"Testing endpoints manually"})," in the SE main guide."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"python-specific-troubleshooting",children:"Python-specific troubleshooting"}),"\n",(0,t.jsx)(n.h3,{id:"proto-changes-have-no-effect",children:"Proto changes have no effect"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Symptom:"})," You edited ",(0,t.jsx)(n.code,{children:"proto/app/service.proto"})," but the generated stubs haven't updated."]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Fix:"})," Re-run the proto generation script:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"./proto.sh\n"})}),"\n",(0,t.jsx)(n.p,{children:"Restart the service after re-generation."}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsxs)(n.h3,{id:"modulenotfounderror-for-app",children:[(0,t.jsx)(n.code,{children:"ModuleNotFoundError"})," for ",(0,t.jsx)(n.code,{children:"app"})]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Symptom:"})," Running ",(0,t.jsx)(n.code,{children:"python -m app"})," gives ",(0,t.jsx)(n.code,{children:"ModuleNotFoundError: No module named 'app'"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Cause:"})," The working directory is wrong. The module lives under ",(0,t.jsx)(n.code,{children:"src/"}),"."]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Fix:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"cd src\npython -m app\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Or set ",(0,t.jsx)(n.code,{children:"PYTHONPATH"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"PYTHONPATH=src python -m app\n"})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsxs)(n.h3,{id:"debugpy-not-found",children:[(0,t.jsx)(n.code,{children:"debugpy"})," not found"]}
1),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Symptom:"})," VS Code says ",(0,t.jsx)(n.code,{children:"debugpy"})," is not installed."]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.strong,{children:"Fix:"})}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"pip install debugpy\n# If using a virtual environment, activate it first:\nsource .venv/bin/activate && pip install debugpy\n"})}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h3,{id:"checking-for-port-conflicts",children:"Checking for port conflicts"}),"\n",(0,t.jsxs)(n.p,{children:["See ",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/local-debugging-python#checking-for-port-conflicts",children:"Checking for port conflicts"}),"\nin the Python language guide."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"ai-assistance",children:"AI assistance"}),"\n",(0,t.jsxs)(n.p,{children:["The app template ships with a Claude agent skill at\n",(0,t.jsx)(n.a,{href:"https://github.com/AccelByte/extend-service-extension-python/tree/main/.claude/skills/debugging-guide/SKILL.md",children:(0,t.jsx)(n.code,{children:".claude/skills/debugging-guide/SKILL.md"})}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["For AI prompting tips and MCP server details, see\n",(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/#debugging-with-ai-assistance",children:"Debugging with AI assistance"})," in the\ngeneric guide."]}),"\n",(0,t.jsx)(n.hr,{}),"\n",(0,t.jsx)(n.h2,{id:"references",children:"References"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/",children:"Extend local debugging guide"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/local-debugging/local-debugging-python",children:"Python language setup guide"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"/gaming-services/modules/foundations/extend/service-extension/debugging-guide/",children:"SE main debugging guide"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://github.com/AccelByte/extend-service-extension-python",children:"Extend Service Extension app template \u2014 Python"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://code.visualstudio.com/docs/python/debugging",children:"Python debugging in VS Code"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://github.com/microsoft/debugpy",children:"debugpy"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://jqlang.org",children:"jq \u2014 JSON processor"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://github.com/fullstorydev/grpcurl",children:"grpcurl"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://docs.accelbyte.io/gaming-services/modules/foundations/extend/service-extension/",children:"AccelByte Extend Service Extension"})}),"\n"]})]})}function h(e={}){let{wrapper:n}={...(0,r.a)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(a,{...e})}):a(e)}},822840:function(e,n,i){i.d(n,{Z:()=>o,a:()=>d});var s=i(202784);let t={},r=s.createContext(t);function d(e){let n=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:d(e.components),s.createElement(r.Provider,{value:n},e.children)}},314537:function(e){e.exports=JSON.parse('{"id":"modules/foundations/extend/service-extension/debugging-guide/debugging-guide-service-extension-python","title":"Local debugging guide \u2014 Python","description":"Step-by-step guide to debugging an Extend Service Extension app written in Python, covering environment setup, debugpy attachment, log reading, and common issue diagnosis.","source":"@site/../../docs/docs-ags/modules/01-foundations/extend/03-service-extension/07-debugging-guide/07-debugging-guide-python.md","sourceDirName":"modules/01-foundations/extend/03-service-extension/07-debugging-guide","slug":"/modules/foundations/extend/service-extension/debugging-guide/debugging-guide-service-extension-python","permalink":"/gaming-services/modules/foundations/extend/service-extension/debugging-guide/debugging-guide-service-extension-python","draft":false,"unlisted":false,"tags":[],"version":"current","lastUpdatedAt":1779734232000,"sidebarPosition":7,"frontMatter":{"id":"debugging-guide-service-extension-python","title":"Local debugging guide \u2014 Python","description":"Step-by-step guide to debugging an Extend Service Extension app written in Python, covering environment setup, debugpy attachment, log reading, and common issue diagnosis.","sidebar_label":"Python","ab_notes":"","ab_teams":"ab_team_extend"},"sidebar":"modulesSidebar","previous":{"title":"Java","permalink":"/gaming-services/modules/foundations/extend/service-extension/debugging-guide/debugging-guide-service-extension-java"},"next":{"title":"Vivox authentication service","permalink":"/gaming-services/modules/foundations/extend/service-extension/sample-use-cases/vivox-authentication-service"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.