[{"data":1,"prerenderedAt":520},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-packages-main-explained":3},{"id":4,"title":5,"body":6,"description":506,"difficulty":507,"extension":508,"framework":509,"frameworkSlug":69,"meta":510,"navigation":162,"order":133,"path":511,"qaPath":512,"seo":513,"stem":514,"subtopic":515,"topic":516,"topicSlug":517,"updated":518,"__hash__":519},"blog\u002Fblog\u002Fpython-packages-main-explained.md","Python Packages & __main__ Explained — __init__.py, Running Modules, and Package Layout",{"type":7,"value":8,"toc":492},"minimark",[9,19,45,49,55,65,95,104,111,121,192,208,218,229,295,298,302,312,342,356,362,372,378,405,415,419,434,440,450,454,488],[10,11,13,14,18],"h2",{"id":12},"python-packages-main-explained","Python packages & ",[15,16,17],"strong",{},"main",", explained",[20,21,22,23,27,28,31,32,35,36,40,41,44],"p",{},"A package is just a directory of modules Python treats as a namespace. The mechanics —\n",[24,25,26],"code",{},"__init__.py",", the ",[24,29,30],{},"__main__"," idiom, and ",[24,33,34],{},"python -m"," — confuse a lot of people because they\ntouch how Python both ",[37,38,39],"em",{},"imports"," and ",[37,42,43],{},"runs"," code. Here's how the pieces fit.",[10,46,48],{"id":47},"what-makes-a-package","What makes a package",[20,50,51,52,54],{},"A package is a directory Python can import. Traditionally it contains an ",[24,53,26],{}," file;\nthat file runs when the package is first imported and defines what the package exposes.",[56,57,62],"pre",{"className":58,"code":60,"language":61},[59],"language-text","myapp\u002F\n    __init__.py        # marks\u002Finitialises the package\n    models.py\n    utils\u002F\n        __init__.py\n        text.py\n","text",[24,63,60],{"__ignoreMap":64},"",[56,66,70],{"className":67,"code":68,"language":69,"meta":64,"style":64},"language-python shiki shiki-themes github-light github-dark","from myapp.utils.text import slugify   # dotted path follows the directory tree\n","python",[24,71,72],{"__ignoreMap":64},[73,74,77,81,85,88,91],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"szBVR","from",[73,82,84],{"class":83},"sVt8B"," myapp.utils.text ",[73,86,87],{"class":79},"import",[73,89,90],{"class":83}," slugify   ",[73,92,94],{"class":93},"sJ8bj","# dotted path follows the directory tree\n",[20,96,97,99,100,103],{},[24,98,26],{}," can be empty, or it can re-export names so callers get a clean API\n(",[24,101,102],{},"from myapp.models import User"," instead of deep paths).",[10,105,107,110],{"id":106},"initpy-and-what-to-put-in-it",[15,108,109],{},"init",".py and what to put in it",[20,112,113,114,116,117,120],{},"The ",[24,115,26],{}," runs on import, so it's the place to set up the package's public surface.\nA common pattern is to lift key names up and declare ",[24,118,119],{},"__all__",".",[56,122,124],{"className":67,"code":123,"language":69,"meta":64,"style":64},"# myapp\u002F__init__.py\nfrom .models import User\nfrom .utils.text import slugify\n\n__all__ = [\"User\", \"slugify\"]    # what \"from myapp import *\" exposes\n",[24,125,126,131,144,157,164],{"__ignoreMap":64},[73,127,128],{"class":75,"line":76},[73,129,130],{"class":93},"# myapp\u002F__init__.py\n",[73,132,134,136,139,141],{"class":75,"line":133},2,[73,135,80],{"class":79},[73,137,138],{"class":83}," .models ",[73,140,87],{"class":79},[73,142,143],{"class":83}," User\n",[73,145,147,149,152,154],{"class":75,"line":146},3,[73,148,80],{"class":79},[73,150,151],{"class":83}," .utils.text ",[73,153,87],{"class":79},[73,155,156],{"class":83}," slugify\n",[73,158,160],{"class":75,"line":159},4,[73,161,163],{"emptyLinePlaceholder":162},true,"\n",[73,165,167,170,173,176,180,183,186,189],{"class":75,"line":166},5,[73,168,119],{"class":169},"sj4cs",[73,171,172],{"class":79}," =",[73,174,175],{"class":83}," [",[73,177,179],{"class":178},"sZZnC","\"User\"",[73,181,182],{"class":83},", ",[73,184,185],{"class":178},"\"slugify\"",[73,187,188],{"class":83},"]    ",[73,190,191],{"class":93},"# what \"from myapp import *\" exposes\n",[20,193,194,195,197,198,200,201,204,205,207],{},"Keep it light — heavy work in ",[24,196,26],{}," runs on every import of the package. Since Python\n3.3, directories without ",[24,199,26],{}," can act as ",[37,202,203],{},"namespace packages",", but explicit\n",[24,206,26],{}," remains the clear default for regular packages.",[10,209,113,211,214,215,217],{"id":210},"the-name-main-idiom",[15,212,213],{},"name"," == \"",[15,216,17],{},"\" idiom",[20,219,220,221,224,225,228],{},"Every module has a ",[24,222,223],{},"__name__",". When run directly it's ",[24,226,227],{},"\"__main__\"","; when imported it's the\nmodule's name. The guard lets a file be both an importable module and a runnable script.",[56,230,232],{"className":67,"code":231,"language":69,"meta":64,"style":64},"# tool.py\ndef main():\n    print(\"running\")\n\nif __name__ == \"__main__\":\n    main()       # runs only when executed directly, not on import\n",[24,233,234,239,251,265,269,286],{"__ignoreMap":64},[73,235,236],{"class":75,"line":76},[73,237,238],{"class":93},"# tool.py\n",[73,240,241,244,248],{"class":75,"line":133},[73,242,243],{"class":79},"def",[73,245,247],{"class":246},"sScJk"," main",[73,249,250],{"class":83},"():\n",[73,252,253,256,259,262],{"class":75,"line":146},[73,254,255],{"class":169},"    print",[73,257,258],{"class":83},"(",[73,260,261],{"class":178},"\"running\"",[73,263,264],{"class":83},")\n",[73,266,267],{"class":75,"line":159},[73,268,163],{"emptyLinePlaceholder":162},[73,270,271,274,277,280,283],{"class":75,"line":166},[73,272,273],{"class":79},"if",[73,275,276],{"class":169}," __name__",[73,278,279],{"class":79}," ==",[73,281,282],{"class":178}," \"__main__\"",[73,284,285],{"class":83},":\n",[73,287,289,292],{"class":75,"line":288},6,[73,290,291],{"class":83},"    main()       ",[73,293,294],{"class":93},"# runs only when executed directly, not on import\n",[20,296,297],{},"Without the guard, the script's code would execute as a side effect of importing it — which\nis also why multiprocessing requires this guard.",[10,299,301],{"id":300},"running-packages-with-m","Running packages with -m",[20,303,304,307,308,311],{},[24,305,306],{},"python -m package"," runs a module ",[37,309,310],{},"as"," a script while keeping proper package context, so\nrelative imports work. This is the right way to run code that lives inside a package.",[56,313,317],{"className":314,"code":315,"language":316,"meta":64,"style":64},"language-bash shiki shiki-themes github-light github-dark","python -m myapp.tool        # runs myapp\u002Ftool.py with myapp as its package\npython script.py            # runs as top-level — relative imports would fail\n","bash",[24,318,319,332],{"__ignoreMap":64},[73,320,321,323,326,329],{"class":75,"line":76},[73,322,69],{"class":246},[73,324,325],{"class":169}," -m",[73,327,328],{"class":178}," myapp.tool",[73,330,331],{"class":93},"        # runs myapp\u002Ftool.py with myapp as its package\n",[73,333,334,336,339],{"class":75,"line":133},[73,335,69],{"class":246},[73,337,338],{"class":178}," script.py",[73,340,341],{"class":93},"            # runs as top-level — relative imports would fail\n",[20,343,344,347,348,351,352,355],{},[24,345,346],{},"-m"," adds the current directory to ",[24,349,350],{},"sys.path"," and sets up the package, which is exactly what\ndirect ",[24,353,354],{},"python path\u002Fto\u002Ffile.py"," execution does not do.",[10,357,359,361],{"id":358},"mainpy-makes-a-package-runnable",[15,360,17],{},".py makes a package runnable",[20,363,364,365,368,369,371],{},"If a package contains a ",[24,366,367],{},"__main__.py",", then ",[24,370,306],{}," runs that file. This lets a\nwhole package be executed like a command.",[56,373,376],{"className":374,"code":375,"language":61},[59],"myapp\u002F\n    __init__.py\n    __main__.py        # python -m myapp runs this\n",[24,377,375],{"__ignoreMap":64},[56,379,381],{"className":67,"code":380,"language":69,"meta":64,"style":64},"# myapp\u002F__main__.py\nfrom .cli import run\nrun()\n",[24,382,383,388,400],{"__ignoreMap":64},[73,384,385],{"class":75,"line":76},[73,386,387],{"class":93},"# myapp\u002F__main__.py\n",[73,389,390,392,395,397],{"class":75,"line":133},[73,391,80],{"class":79},[73,393,394],{"class":83}," .cli ",[73,396,87],{"class":79},[73,398,399],{"class":83}," run\n",[73,401,402],{"class":75,"line":146},[73,403,404],{"class":83},"run()\n",[20,406,407,408,40,411,414],{},"This is how tools like ",[24,409,410],{},"python -m http.server",[24,412,413],{},"python -m pip"," work.",[10,416,418],{"id":417},"modern-project-layout","Modern project layout",[20,420,421,422,425,426,429,430,433],{},"Real projects use a ",[15,423,424],{},"src layout"," with a ",[24,427,428],{},"pyproject.toml"," declaring the package. Keeping\ncode under ",[24,431,432],{},"src\u002F"," prevents accidentally importing from the working directory instead of the\ninstalled package.",[56,435,438],{"className":436,"code":437,"language":61},[59],"myproject\u002F\n    pyproject.toml\n    src\u002F\n        myapp\u002F\n            __init__.py\n            ...\n    tests\u002F\n",[24,439,437],{"__ignoreMap":64},[20,441,442,443,182,446,449],{},"After ",[24,444,445],{},"pip install -e .",[24,447,448],{},"import myapp"," works from anywhere, and tests run against the\ninstalled package rather than loose files.",[10,451,453],{"id":452},"recap","Recap",[20,455,456,457,460,461,465,466,471,472,476,477,481,482,484,485,487],{},"A ",[15,458,459],{},"package"," is a directory of modules, marked and initialised by ",[15,462,463],{},[24,464,26],{}," (run on\nimport — keep it light; use it to re-export a clean API). The ",[15,467,468],{},[24,469,470],{},"__name__ == \"__main__\"","\nguard lets a file act as both an importable module and a runnable script. Run code inside\npackages with ",[15,473,474],{},[24,475,306],{}," so relative imports work, and add a ",[15,478,479],{},[24,480,367],{},"\nto make a package itself executable. For real projects, adopt the ",[15,483,424],{}," with\n",[24,486,428],{}," and an editable install.",[489,490,491],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":64,"searchDepth":133,"depth":133,"links":493},[494,496,497,499,501,502,504,505],{"id":12,"depth":133,"text":495},"Python packages & main, explained",{"id":47,"depth":133,"text":48},{"id":106,"depth":133,"text":498},"init.py and what to put in it",{"id":210,"depth":133,"text":500},"The name == \"main\" idiom",{"id":300,"depth":133,"text":301},{"id":358,"depth":133,"text":503},"main.py makes a package runnable",{"id":417,"depth":133,"text":418},{"id":452,"depth":133,"text":453},"How Python packages work — __init__.py and what it's for, the __name__ == \"__main__\" idiom, running packages with python -m and __main__.py, and modern src-layout project structure.","medium","md","Python",{},"\u002Fblog\u002Fpython-packages-main-explained","\u002Fpython\u002Fmodules\u002Fpackages",{"title":5,"description":506},"blog\u002Fpython-packages-main-explained","Packages & __main__","Modules, Packages & Environments","modules","2026-06-19","qx9gmnPOlUMsVYT4mxhwHrSAU-Sr9yK-R43oAyv2m4c",1782244093018]