PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/doc/python3.11-setuptools/userguide
Viewing File: /usr/share/doc/python3.11-setuptools/userguide/package_discovery.rst
.. _`package_discovery`: ======================================== Package Discovery and Namespace Packages ======================================== .. note:: a full specification for the keywords supplied to ``setup.cfg`` or ``setup.py`` can be found at :doc:`keywords reference </references/keywords>` .. important:: The examples provided here are only to demonstrate the functionality introduced. More metadata and options arguments need to be supplied if you want to replicate them on your system. If you are completely new to setuptools, the :doc:`quickstart` section is a good place to start. ``Setuptools`` provides powerful tools to handle package discovery, including support for namespace packages. Normally, you would specify the packages to be included manually in the following manner: .. tab:: setup.cfg .. code-block:: ini [options] #... packages = mypkg mypkg.subpkg1 mypkg.subpkg2 .. tab:: setup.py .. code-block:: python setup( # ... packages=['mypkg', 'mypkg.subpkg1', 'mypkg.subpkg2'] ) .. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml # ... [tool.setuptools] packages = ["mypkg", "mypkg.subpkg1", "mypkg.subpkg2"] # ... If your packages are not in the root of the repository or do not correspond exactly to the directory structure, you also need to configure ``package_dir``: .. tab:: setup.cfg .. code-block:: ini [options] # ... package_dir = = src # directory containing all the packages (e.g. src/mypkg, src/mypkg/subpkg1, ...) # OR package_dir = mypkg = lib # mypkg.module corresponds to lib/module.py mypkg.subpkg1 = lib1 # mypkg.subpkg1.module1 corresponds to lib1/module1.py mypkg.subpkg2 = lib2 # mypkg.subpkg2.module2 corresponds to lib2/module2.py # ... .. tab:: setup.py .. code-block:: python setup( # ... package_dir = {"": "src"} # directory containing all the packages (e.g. src/mypkg, src/mypkg/subpkg1, ...) ) # OR setup( # ... package_dir = { "mypkg": "lib", # mypkg.module corresponds to lib/mod.py "mypkg.subpkg1": "lib1", # mypkg.subpkg1.module1 corresponds to lib1/module1.py "mypkg.subpkg2": "lib2" # mypkg.subpkg2.module2 corresponds to lib2/module2.py # ... ) .. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml [tool.setuptools] # ... package-dir = {"" = "src"} # directory containing all the packages (e.g. src/mypkg1, src/mypkg2) # OR [tool.setuptools.package-dir] mypkg = "lib" # mypkg.module corresponds to lib/module.py "mypkg.subpkg1" = "lib1" # mypkg.subpkg1.module1 corresponds to lib1/module1.py "mypkg.subpkg2" = "lib2" # mypkg.subpkg2.module2 corresponds to lib2/module2.py # ... This can get tiresome really quickly. To speed things up, you can rely on setuptools automatic discovery, or use the provided tools, as explained in the following sections. .. important:: Although ``setuptools`` allows developers to create a very complex mapping between directory names and package names, it is better to *keep it simple* and reflect the desired package hierarchy in the directory structure, preserving the same names. .. _auto-discovery: Automatic discovery =================== .. warning:: Automatic discovery is a **beta** feature and might change in the future. See :ref:`custom-discovery` for other methods of discovery. By default ``setuptools`` will consider 2 popular project layouts, each one with its own set of advantages and disadvantages [#layout1]_ [#layout2]_ as discussed in the following sections. Setuptools will automatically scan your project directory looking for these layouts and try to guess the correct values for the :ref:`packages <declarative config>` and :doc:`py_modules </references/keywords>` configuration. .. important:: Automatic discovery will **only** be enabled if you **don't** provide any configuration for ``packages`` and ``py_modules``. If at least one of them is explicitly set, automatic discovery will not take place. **Note**: specifying ``ext_modules`` might also prevent auto-discover from taking place, unless your opt into :doc:`pyproject_config` (which will disable the backward compatible behaviour). .. _src-layout: src-layout ---------- The project should contain a ``src`` directory under the project root and all modules and packages meant for distribution are placed inside this directory:: project_root_directory ├── pyproject.toml # AND/OR setup.cfg, setup.py ├── ... └── src/ └── mypkg/ ├── __init__.py ├── ... ├── module.py ├── subpkg1/ │   ├── __init__.py │   ├── ... │   └── module1.py └── subpkg2/ ├── __init__.py ├── ... └── module2.py This layout is very handy when you wish to use automatic discovery, since you don't have to worry about other Python files or folders in your project root being distributed by mistake. In some circumstances it can be also less error-prone for testing or when using :pep:`420`-style packages. On the other hand you cannot rely on the implicit ``PYTHONPATH=.`` to fire up the Python REPL and play with your package (you will need an `editable install`_ to be able to do that). .. _flat-layout: flat-layout ----------- *(also known as "adhoc")* The package folder(s) are placed directly under the project root:: project_root_directory ├── pyproject.toml # AND/OR setup.cfg, setup.py ├── ... └── mypkg/ ├── __init__.py ├── ... ├── module.py ├── subpkg1/ │   ├── __init__.py │   ├── ... │   └── module1.py └── subpkg2/ ├── __init__.py ├── ... └── module2.py This layout is very practical for using the REPL, but in some situations it can be more error-prone (e.g. during tests or if you have a bunch of folders or Python files hanging around your project root). To avoid confusion, file and folder names that are used by popular tools (or that correspond to well-known conventions, such as distributing documentation alongside the project code) are automatically filtered out in the case of *flat-layout*: .. autoattribute:: setuptools.discovery.FlatLayoutPackageFinder.DEFAULT_EXCLUDE .. autoattribute:: setuptools.discovery.FlatLayoutModuleFinder.DEFAULT_EXCLUDE .. warning:: If you are using auto-discovery with *flat-layout*, ``setuptools`` will refuse to create :term:`distribution archives <Distribution Package>` with multiple top-level packages or modules. This is done to prevent common errors such as accidentally publishing code not meant for distribution (e.g. maintenance-related scripts). Users that purposefully want to create multi-package distributions are advised to use :ref:`custom-discovery` or the ``src-layout``. There is also a handy variation of the *flat-layout* for utilities/libraries that can be implemented with a single Python file: single-module distribution ^^^^^^^^^^^^^^^^^^^^^^^^^^ A standalone module is placed directly under the project root, instead of inside a package folder:: project_root_directory ├── pyproject.toml # AND/OR setup.cfg, setup.py ├── ... └── single_file_lib.py .. _custom-discovery: Custom discovery ================ If the automatic discovery does not work for you (e.g., you want to *include* in the distribution top-level packages with reserved names such as ``tasks``, ``example`` or ``docs``, or you want to *exclude* nested packages that would be otherwise included), you can use the provided tools for package discovery: .. tab:: setup.cfg .. code-block:: ini [options] packages = find: #or packages = find_namespace: .. tab:: setup.py .. code-block:: python from setuptools import find_packages # or from setuptools import find_namespace_packages .. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml # ... [tool.setuptools.packages] find = {} # Scanning implicit namespaces is active by default # OR find = {namespaces = false} # Disable implicit namespaces Finding simple packages ----------------------- Let's start with the first tool. ``find:`` (``find_packages()``) takes a source directory and two lists of package name patterns to exclude and include, and then returns a list of ``str`` representing the packages it could find. To use it, consider the following directory:: mypkg ├── pyproject.toml # AND/OR setup.cfg, setup.py └── src ├── pkg1 │   └── __init__.py ├── pkg2 │   └── __init__.py ├── additional │   └── __init__.py └── pkg └── namespace └── __init__.py To have setuptools to automatically include packages found in ``src`` that start with the name ``pkg`` and not ``additional``: .. tab:: setup.cfg .. code-block:: ini [options] packages = find: package_dir = =src [options.packages.find] where = src include = pkg* # alternatively: `exclude = additional*` .. note:: ``pkg`` does not contain an ``__init__.py`` file, therefore ``pkg.namespace`` is ignored by ``find:`` (see ``find_namespace:`` below). .. tab:: setup.py .. code-block:: python setup( # ... packages=find_packages( where='src', include=['pkg*'], # alternatively: `exclude=['additional*']` ), package_dir={"": "src"} # ... ) .. note:: ``pkg`` does not contain an ``__init__.py`` file, therefore ``pkg.namespace`` is ignored by ``find_packages()`` (see ``find_namespace_packages()`` below). .. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml [tool.setuptools.packages.find] where = ["src"] include = ["pkg*"] # alternatively: `exclude = ["additional*"]` namespaces = false .. note:: When using ``tool.setuptools.packages.find`` in ``pyproject.toml``, setuptools will consider :pep:`implicit namespaces <420>` by default when scanning your project directory. To avoid ``pkg.namespace`` from being added to your package list you can set ``namespaces = false``. This will prevent any folder without an ``__init__.py`` file from being scanned. .. important:: ``include`` and ``exclude`` accept strings representing :mod:`glob` patterns. These patterns should match the **full** name of the Python module (as if it was written in an ``import`` statement). For example if you have ``util`` pattern, it will match ``util/__init__.py`` but not ``util/files/__init__.py``. The fact that the parent package is matched by the pattern will not dictate if the submodule will be included or excluded from the distribution. You will need to explicitly add a wildcard (e.g. ``util*``) if you want the pattern to also match submodules. .. _Namespace Packages: Finding namespace packages -------------------------- ``setuptools`` provides ``find_namespace:`` (``find_namespace_packages()``) which behaves similarly to ``find:`` but works with namespace packages. Before diving in, it is important to have a good understanding of what :pep:`namespace packages <420>` are. Here is a quick recap. When you have two packages organized as follows: .. code-block:: bash /Users/Desktop/timmins/foo/__init__.py /Library/timmins/bar/__init__.py If both ``Desktop`` and ``Library`` are on your ``PYTHONPATH``, then a namespace package called ``timmins`` will be created automatically for you when you invoke the import mechanism, allowing you to accomplish the following: .. code-block:: pycon >>> import timmins.foo >>> import timmins.bar as if there is only one ``timmins`` on your system. The two packages can then be distributed separately and installed individually without affecting the other one. Now, suppose you decide to package the ``foo`` part for distribution and start by creating a project directory organized as follows:: foo ├── pyproject.toml # AND/OR setup.cfg, setup.py └── src └── timmins └── foo └── __init__.py If you want the ``timmins.foo`` to be automatically included in the distribution, then you will need to specify: .. tab:: setup.cfg .. code-block:: ini [options] package_dir = =src packages = find_namespace: [options.packages.find] where = src ``find:`` won't work because ``timmins`` doesn't contain ``__init__.py`` directly, instead, you have to use ``find_namespace:``. You can think of ``find_namespace:`` as identical to ``find:`` except it would count a directory as a package even if it doesn't contain ``__init__.py`` file directly. .. tab:: setup.py .. code-block:: python setup( # ... packages=find_namespace_packages(where='src'), package_dir={"": "src"} # ... ) When you use ``find_packages()``, all directories without an ``__init__.py`` file will be disconsidered. On the other hand, ``find_namespace_packages()`` will scan all directories. .. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml [tool.setuptools.packages.find] where = ["src"] When using ``tool.setuptools.packages.find`` in ``pyproject.toml``, setuptools will consider :pep:`implicit namespaces <420>` by default when scanning your project directory. After installing the package distribution, ``timmins.foo`` would become available to your interpreter. .. warning:: Please have in mind that ``find_namespace:`` (setup.cfg), ``find_namespace_packages()`` (setup.py) and ``find`` (pyproject.toml) will scan **all** folders that you have in your project directory if you use a :ref:`flat-layout`. If used naïvely, this might result in unwanted files being added to your final wheel. For example, with a project directory organized as follows:: foo ├── docs │ └── conf.py ├── timmins │ └── foo │ └── __init__.py └── tests └── tests_foo └── __init__.py final users will end up installing not only ``timmins.foo``, but also ``docs`` and ``tests.tests_foo``. A simple way to fix this is to adopt the aforementioned :ref:`src-layout`, or make sure to properly configure the ``include`` and/or ``exclude`` accordingly. .. tip:: After :ref:`building your package <building>`, you can have a look if all the files are correct (nothing missing or extra), by running the following commands: .. code-block:: bash tar tf dist/*.tar.gz unzip -l dist/*.whl This requires the ``tar`` and ``unzip`` to be installed in your OS. On Windows you can also use a GUI program such as 7zip_. Legacy Namespace Packages ========================= The fact you can create namespace packages so effortlessly above is credited to `PEP 420 <https://www.python.org/dev/peps/pep-0420/>`_. It used to be more cumbersome to accomplish the same result. Historically, there were two methods to create namespace packages. One is the ``pkg_resources`` style supported by ``setuptools`` and the other one being ``pkgutils`` style offered by ``pkgutils`` module in Python. Both are now considered *deprecated* despite the fact they still linger in many existing packages. These two differ in many subtle yet significant aspects and you can find out more on `Python packaging user guide <https://packaging.python.org/guides/packaging-namespace-packages/>`_. ``pkg_resource`` style namespace package ---------------------------------------- This is the method ``setuptools`` directly supports. Starting with the same layout, there are two pieces you need to add to it. First, an ``__init__.py`` file directly under your namespace package directory that contains the following: .. code-block:: python __import__("pkg_resources").declare_namespace(__name__) And the ``namespace_packages`` keyword in your ``setup.cfg`` or ``setup.py``: .. tab:: setup.cfg .. code-block:: ini [options] namespace_packages = timmins .. tab:: setup.py .. code-block:: python setup( # ... namespace_packages=['timmins'] ) And your directory should look like this .. code-block:: bash foo ├── pyproject.toml # AND/OR setup.cfg, setup.py └── src └── timmins ├── __init__.py └── foo └── __init__.py Repeat the same for other packages and you can achieve the same result as the previous section. ``pkgutil`` style namespace package ----------------------------------- This method is almost identical to the ``pkg_resource`` except that the ``namespace_packages`` declaration is omitted and the ``__init__.py`` file contains the following: .. code-block:: python __path__ = __import__('pkgutil').extend_path(__path__, __name__) The project layout remains the same and ``pyproject.toml/setup.cfg`` remains the same. ---- .. [#beta] Support for adding build configuration options via the ``[tool.setuptools]`` table in the ``pyproject.toml`` file is still in **beta** stage. See :doc:`/userguide/pyproject_config`. .. [#layout1] https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure .. [#layout2] https://blog.ionelmc.ro/2017/09/25/rehashing-the-src-layout/ .. _editable install: https://pip.pypa.io/en/stable/cli/pip_install/#editable-installs .. _7zip: https://www.7-zip.org