TJc@sddkZddkZddkZddkZddkZddkZddkZddkZddkZddk Z yddk l Z Wn#e j oddk l Z nXddk Z ddklZddklZddklZlZlZddklZdefdYZd efd YZd efd YZd efdYZdefdYZdefdYZdefdYZdefdYZ dS(iN(tStringIO(tCommand(tlog(tregistrytmerge_resourcest_JavascriptFileIter(t OrderedSettarchive_tw_resourcesc BsweZdZdZdddd d!d"d#gZdgZeZdZ dZ dZ dZ dZ dZRS($s Setuptools command to copy and optionally compress all static resources from a series of distributions and their dependencies into a directory where they can be served by a fast web server. To enable compression of CSS and JS files you will need to have installed a Java Runtime Environment and YUICompressor (http://www.julienlecomte.net/yuicompressor) In order for resources from widget eggs to be properly collected these need to have a 'toscawidgets.widgets' 'widgets' entry-point which points to a module which, when imported, instantiates all needed JS and CSS Links. The result is laid out in the output directory in such a way that when a a web server such as Apache or Nginx is configured to map URLS that begin with /toscawidgets to that directory static files will be served from there bypassing python completely. To integrate this command into your build process you can add these lines to ``setup.cfg``:: [archive_tw_resources] output = /home/someuser/public_html/toscawidgets/ compresslevel = 2 distributions = MyProject yuicompressor = /home/someuser/bin/yuicompressor.jar onepass = true [aliases] deploy = archive_tw_resources --force install This way you can run:: $ python setup.py deploy To install a new version of your app and copy/compress resources. s]Copies ToscaWidgets static resources into a directory where a fast web-server can serve them.soutput=tos9Output directory. If it doesn't exist it will be created.tforcetfs+If output dir exists, it will be ovewrittentonepasssIf given, yuicompressor will only be called once for each kind of file with a all files together and then separated back into smaller filesscompresslevel=tcsgCompression level: 0) for no compression (default). 1) for js-minification. 2) for js & css compressionsyuicompressor=sName of the yuicompressor jar.sdistributions=tdsList of widget dists. to include resources from (dependencies will be handled recursively). Note that these distributions need to define a 'toscawidgets.widgets' 'widgets' entrypoint pointing to a a module where resources are located.t requireoncetrs;Surround the gathered Javascript with a require_once-guard.s.svncCsCd|_t|_t|_d|_g|_d|_t|_dS(Ntisyuicompressor.jar(toutputtFalseR R t compresslevelt distributionst yuicompressorR(tself((s3/usr/lib/python2.6/site-packages/tw/core/command.pytinitialize_optionscs      cCsU|id|id|idt|i|_tii|i|_dS(NRRR(t ensure_stringtensure_string_listtintRtostpathtabspathR(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pytfinalize_optionsms    cCs|iptidIJdS|iptidIJdStii|io(|i otid|idIJdS|idjo0tii|i  otid|i IJdSt i |_ }|i ti|fd||idjo6|iot|||_q>t|||_nt|||_|i |itd|iitii|io'|i ti|ifd |in|i ti|ifd tii|itiid }|i ti||fd |dS( Ns#Need to specify an output directorys)Need to specify at least one distributionsDestination dir %s exists. sUse -f to ovewriteis Could not find YUICompressor at sCreating temp dir %ssExtracting resourcessDeleting old output dir %ssCreating output dirt/sMoving build to %s(RtsyststderrRRRtexistsR RRttempfiletmktempttempdirtexecutetmakedirsR tOnePassCompressingWritertwritertCompressingWritert FileWritert_copy_resourcesttupletfinalizetshutiltrmtreetjoinRtprefixtstriptmove(RR%t final_dest((s3/usr/lib/python2.6/site-packages/tw/core/command.pytrunts<    !'    $c Csypg}ti|iD]}||iq~}t|i|ti|dd}|id|iWn&t j o}|id|nXdS(Nstoscawidgets.widgetstwidgetss Loaded %ss%s has no widgets entrypoint( t pkg_resourcestget_distributiontrequirest project_nametmapt _load_widgetstload_entry_pointtannouncet__name__t ImportError(Rt distributiont_[1]RR:tmodte((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR=s,  cCst|i|ixutD]m\}}td|id}|d}di|d}|i|i ||fd||i i fqWdS(NRiisCopying %s recursively into %s( R<R=RRtfiltertNonetsplitR1R&t_copy_resource_treeR)tbase(Rtwebdirtdirnametpartstmodnametfname((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR,s  c Cs?yxti||D]}||ijoqndi||f}di||f}ti||o$|i|i||fd|qti||}ti |\}}d}|i o#|djot i ||}nti||} di||f} |i|ii| | fd| |dj otii|i| } t| } ti} t| d} | it i|| i| i| it i|| iti| | n| iqWWnLtj o@}|itijo$|i dt!|||fq;nXdS(NRsRecursing into sapplication/javascripts Processing twsCould not copy %s("R8tresource_listdirt IGNORED_NAMESR1tresource_isdirR&RItresource_filenamet mimetypest guess_typeRGRRt _marker_nametresource_streamR)t write_fileRRR%topenR#R$twritetSTART_TEMPLATEtreadt END_TEMPLATEtclosetrenametOSErrorterrnotENOENTtwarntrepr(RRNROtnametrel_namet full_nametctt_t require_oncetstreamtfilenametinftoutnametoutfRE((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRIsB     (soutput=Rs9Output directory. If it doesn't exist it will be created.(sforceR s+If output dir exists, it will be ovewrittenN(sonepassNsIf given, yuicompressor will only be called once for each kind of file with a all files together and then separated back into smaller files(scompresslevel=R sgCompression level: 0) for no compression (default). 1) for js-minification. 2) for js & css compression(syuicompressor=NsName of the yuicompressor jar.(sdistributions=R sList of widget dists. to include resources from (dependencies will be handled recursively). Note that these distributions need to define a 'toscawidgets.widgets' 'widgets' entrypoint pointing to a a module where resources are located.(s requireonceRs;Surround the gathered Javascript with a require_once-guard.(R@t __module__t__doc__t descriptionRGt user_optionsRRtobjectt NO_VARIANTRRR6R=R,RI(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRs*&     % tResourceAggregatorcBs\eZdZdZeedZdZdZdZdZ dZ dZ RS( st The aggregation of files is delegated to instances of this class. That allows for pre/postprocessing. cCsU|i}|djotit|S|djotit|StddS(Ntjstcsss-Unknown resource kind, must be 'js' or 'css'.(tkindRut__new__tJSResourceAggregatortCSSResourceAggregatort Exception(tclstcommandRmRz((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR{s    cCsQ||_||_g|_|ot|id|_n||_||_dS(NRP(Rtout_namet added_filesRZRptimmediate_writetadd_separator_comments(RRRmRR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt__init__s    cCs|i||}tii|p|iid||fdS|ii||f|ioyt |d}t i }|ii i |||io|iid||fn|iit |indS(Ns WARNING: missing resource: %s.%stUs // -- %s %s -- (tresolve_filenameRRR"RR?RtappendRRZR#R$R)RYRRpR[R](RRNRmtinf_nameRlttempname((s3/usr/lib/python2.6/site-packages/tw/core/command.pytadd_files   cCs t|iS(N(tboolR(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt __nonzero__scCs|io|iindS(N(RRpR_(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pytflushs cCs)x"|iD]}|id|q WdS(Ns%s|%s (RR[(RRptentry((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt write_mapfile s cCsti||}|S(N(R8RT(RRNRmR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRscCsdS(N((Rt out_filename((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt post_hooks( R@RqRrR{tTrueRRRRRRR(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRws      R|cBseZRS((R@Rq(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR|sR}cBs2eZdZdZdZdZdZRS(sv This class allows to splice in the abl.cssprocessor.CSSRewriter so that image-references are re-written. cCst}t|_|iot}t|_ntt|i||||io?ddkl}ddk l }||}|||_ ndS(Ni(tURI(t CSSRewriter( RRtuse_css_rewritertrewritetsuperR}Rt abl.vpathRtabl.cssprocessor.rewriterRtrewriter(RRRmRRRR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR#s     cCsctt|i|||io<ddkl}||i||}|ii|ndS(Ni(R( RR}RRRRRRtadd_css(RRNRmRtcss_file((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR3s  cCs2tt|i|io|iindS(N(RR}RRRR(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR;s cCsZ|ioLddkl}||i}|ii}|idi|dndS(Ni(RtimagesR(RRRRLRRtcopy(RRRtout_dirR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRAs   (R@RqRrRRRR(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR}s    taggregate_tw_resourcesc BsteZdZdZd!d#d$d%d&d'd(d)d*g ZdgZeZdZ dZ dZ dZ d Z RS(+s Setuptools commmand to aggregate Javascript- or CSS-files to one large file, possibly compressed through the use of YUICompressor. To enable compression of CSS and JS files you will need to have installed a Java Runtime Environment and YUICompressor (http://www.julienlecomte.net/yuicompressor) In order for resources from widget eggs to be properly collected these need to have a 'toscawidgets.widgets' 'widgets' entry-point which points to a module which, when imported, instantiates all needed JS and CSS Links. The aggregated resources are served via the :class:`tw.mods.base.HostFramework` and thus must be placed inside a python package to be served as normal resources. An example commandline invocation would look like this:: python2.5 setup.py aggregate_tw_resources -o myproject/public/javascript/aggregated/ -d MyProject --package=myproject s<Aggregates ToscaWidgets static resources into a single file.soutput=Rs9Output directory. If it doesn't exist it will be created.R sIf given, yuicompressor will only be called once for each kind of file with a all files together and then separated back into smaller filesscompresslevel=R sgCompression level: 0) for no compression (default). 1) for js-minification. 2) for js & css compressionsyuicompressor=sName of the yuicompressor jar.sdistributions=R sList of widget dists. to include resources from (dependencies will be handled recursively). Note that these distributions need to define a 'toscawidgets.widgets' 'widgets' entrypoint pointing to a a module where resources are located.skind=tksThe kind, either js or csssvariant=tpsThe registry variant to fetch the data from. If this option is used,only the selected kind of resources is gathered. The resulting fileset is processed and concatenated to a large file.spackage=tmsIf gathering a variant, this option can be used to filter the output for a given package. The name of the package can also contain path-elements.So you can group together e.g. 'foo.bar/public/javascript/subdir'.RRsUse the CSS-rewriter to rewrite relative url/image-references before producing the output. To make this actually work, make user you have the AbletonCSSProcessor installed.s.svncCsad|_t|_t|_d|_t|_d|_g|_ti |_ d|_ d|_ dS(NRiRxsyuicompressor.jar(RRR R RRRzRRtDEFAULT_VARIANTtvariantRGtpackageR(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRs         cCsU|id|id|idt|i|_tii|i|_dS(NRRR(RRRRRRRR(R((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRs    cCs|iptidIJdS|iptidIJdStii|ip(|id|iti|in|i djo0tii|i  otid|i IJdSt i }|i ti|fd||i djo6|iot|||_q7t|||_nt|||_d}xU|iD]J}ti|dd }|dj o"d }|||i}qJqJW|i |i|fd |idS( Ns#Need to specify an output directorys)Need to specify at least one distributionsCreating output dir %sis Could not find YUICompressor at sCreating temp dir %scSstS(N(R(tresource((s3/usr/lib/python2.6/site-packages/tw/core/command.pytresource_filtersstoscawidgets.widgetstresource_aggregation_filtercsfd}|S(Ncs|o |S(N((R(tatb(s3/usr/lib/python2.6/site-packages/tw/core/command.pyRs((RRR((RRs3/usr/lib/python2.6/site-packages/tw/core/command.pytcombine_predicatesss#Collection resources for variant %s(RR R!RRRR"R?tmkdirRRR#R$R&R'R R(R)R*R+R8tget_entry_infoRGtloadt_collect_variant_resourcesR(RR%Rtdistribution_nametepR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR6s8    '        c Csypg}ti|iD]}||iq~}t|i|ti|dd}|id|iWn&t j o}|id|nXdS(Nstoscawidgets.widgetsR7s Loaded %ss%s has no widgets entrypoint( R8R9R:R;R<R=R>R?R@RA(RRBRCRR:RDRE((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR=s,  c sti}t|i|iti||iddj o)djoidd\nfd}t i }t ||}d}t t id|}g}xT|D]L} xC| idD]1} || o| |jo|i| qqWqWddkl} l} x|D]} t| | | foq6n| i} | djoq6nt| toh| t i6} n|i| jo| |i}n| t i}tii|\}}|id|ijoq6n|| |pq6n| i}|id ||f|i||q6W|oC|i t!|}t"i#}x4t$o,|i%d }|pPn|i&|qxW|i'dd!}d ||i|if}tii(|i)|}ti*|||id |t+i,d ||i|if}tii(|i)|}t!|d}|i-||i.|i/||id|t+i,ndS(NRicsIdjotS|iio djotS|iStS(N(RGRRNt startswithR(twidgetRm(tfilebaseR(s3/usr/lib/python2.6/site-packages/tw/core/command.pyt in_packages  cSs7|i}t|to|ti}n|i|fS(N(Rmt isinstancetdictRRRN(RRm((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt widget_names tkeytheadi(tAggregatedJSLinktAggregatedCSSLinksProcessing %s %sis%s-%s.%ss!Created concatenatenated file: %ss %s-%s.%s.mapRPsCreated mapping file: %s(0RtgetcwdR<R=RtchdirRRGRHR#R$RwtsortedRt_widgetstretrieve_resourcesRttw.apiRRRRmt basestringRRRtsplitexttlowerRzRNR?RRRZtmd5tnewRR]tupdatet hexdigestR1RR`RtINFORR_R(RRtcwdRtvariant_filenametresource_aggregatorRR7tdependency_ordered_widgetsRRRRtvariant_mappingRmRjtextRNRnthashtblockthext dest_nameRp((RRs3/usr/lib/python2.6/site-packages/tw/core/command.pyRs|               (soutput=Rs9Output directory. If it doesn't exist it will be created.N(sonepassNsIf given, yuicompressor will only be called once for each kind of file with a all files together and then separated back into smaller files(scompresslevel=R sgCompression level: 0) for no compression (default). 1) for js-minification. 2) for js & css compression(syuicompressor=NsName of the yuicompressor jar.(sdistributions=R sList of widget dists. to include resources from (dependencies will be handled recursively). Note that these distributions need to define a 'toscawidgets.widgets' 'widgets' entrypoint pointing to a a module where resources are located.(skind=RsThe kind, either js or css(svariant=RsThe registry variant to fetch the data from. If this option is used,only the selected kind of resources is gathered. The resulting fileset is processed and concatenated to a large file.(spackage=RsIf gathering a variant, this option can be used to filter the output for a given package. The name of the package can also contain path-elements.So you can group together e.g. 'foo.bar/public/javascript/subdir'.(srewriteRsUse the CSS-rewriter to rewrite relative url/image-references before producing the output. To make this actually work, make user you have the AbletonCSSProcessor installed.(R@RqRrRsRGRtRRRuRvRRR6R=R(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRJs2     . R+cBsIeZdZdZdZx#diD]ZdedUq.WRS(cCs||_||_dS(N(RJtcmd(RRRJ((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR;s cCsdS(N((R((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR.?scCstii|i|}tiitii|ptitii|nt|d}|id|t i |||i dS(Ntwbs Writing %s( RRR1RJR"RLR'RZR?R/t copyfileobjR_(RRlRtfinaltdest((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRYBsswarn announce error executesKdef %(name)s(self, *args, **kw): return self.cmd.%(name)s(*args, **kw) N(R@RqRR.RYRHRftlocals(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR+:s    R*cBs,eZdZdZdZdZRS(cOs&tt|i||d|_dS(Ni(ii(RR*Rtcounters(Rtargstkw((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRUscCsNy4tti|id}d|}|i|Wntj onXdS(Nids2Total JS&CSS compressed size is %.2f%% of original(treducetoperatorttruedivRR?tZeroDivisionError(Rtavgtmsg((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR.Ys  c Cs|idd}|djo|Sdd|iid|g}|iidjo|id n|id ti|d tid tid ti}|id|t }t i |||i }|p|S|i |\}} |idjoX|id||if|idtii| tii||idnvt|t|f} tti| } ttt|i| |_d|| df} |i| t |}|S(Nt.iRyRxtjavas-jars--typeis --nomunges--charset=utf8tstdouttstdinR!sCompressing %sisFailed to compress %s: %dsFile will be copied untoucheds Compressed %s (New size: %.2f%%)id(scsssjs(RHRRRRt subprocesstPopentPIPER?RR/Rtgetvaluet communicatet returncodeRdR R!R[tseektlenRRRR<tsumtzipR( RRlRttypRRtbuffertdataRR!tcounttratioR((s3/usr/lib/python2.6/site-packages/tw/core/command.pytcompressbs<         cCs+|i||}tt|i||S(N(RRR*RY(RRlR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRYs(R@RqRR.RRY(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR*Ss  !R(cBs,eZdZdZdZdZRS(cOsQtt|i||htd6|_d|_d}ti||_dS(NRxs'/*! MARKER #### %(path)s #### MARKER */s1^\/\* MARKER #### (?P.*?) #### MARKER \*\/$( RR(RRt_cachest_markertretcompilet_re(RRRtregexp((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRs  cCsd}t}|idx|D]w}|ii|}|oK|o1|idti||||idn|id}q#|i |q#WdS(NiR( RGRRRtmatchR+RYttruncatetgroupR[(RRltcur_fileRtlineR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyt _demultiplexs   cCst|idxM|iiD]<\}}|id|i|d|}|i|qWtt|idS(NsCompressing all defered filesis __defered__.( R?Rt iteritemsRRRRR(R.(RRtcachet compressed((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR.s   cCs|idd}|ii|}|p(|id|ti|||tS||itIJ|id|t i ||dS(NRis Will not consider %s for onepasss'Defering %s for compression in one pass( RHRtgetR?R*RYRkRRR/R(RRlRRR((s3/usr/lib/python2.6/site-packages/tw/core/command.pyRYs(R@RqRRR.RY(((s3/usr/lib/python2.6/site-packages/tw/core/command.pyR(s   (!RbRRR/R RR#RRRUt cStringIORRAR8t setuptoolsRt distutilsRttw.core.resourcesRRRt tw.core.utilRRRuRwR|R}RR+R*R((((s3/usr/lib/python2.6/site-packages/tw/core/command.pyts4           D-4