{"id":380,"date":"2008-11-03T14:52:57","date_gmt":"2008-11-03T18:52:57","guid":{"rendered":"http:\/\/crazedmonkey.com\/blog\/?p=380"},"modified":"2008-11-03T15:00:10","modified_gmt":"2008-11-03T19:00:10","slug":"pkg_resources-with-py2exe","status":"publish","type":"post","link":"http:\/\/crazedmonkey.com\/blog\/python\/pkg_resources-with-py2exe.html","title":{"rendered":"Extend py2exe to copy files to the zipfile where pkg_resources can load them"},"content":{"rendered":"<p>In creating a Windows PDF generation app for a client of mine, I ran into a limitation of <a href=\"http:\/\/py2exe.org\">py2exe<\/a> which prevents resource loading with <a href=\"http:\/\/peak.telecommunity.com\/DevCenter\/PkgResources\">pkg_resources<\/a>. The module attempts to load resources from py2exe&#8217;s <code>library.zip<\/code>. As the <code>zipfile<\/code> is reserved for compiled bytecode, there&#8217;s no option to copy files into it. You can copy files using py2exe&#8217;s <code>data_files<\/code>, but not into the <code>zipfile<\/code>, and <code>package_data<\/code> isn&#8217;t honoured.  You can, however, extend the <code>py2exe<\/code> class, and that&#8217;s what I did.<\/p>\n<p>The best entry point I could find was <code>copy_extensions()<\/code>. You can override that method to copy files into the build directory. You&#8217;ll want to create the necessary directories if they don&#8217;t already exist and then register the copied files for copying to <code>dist<\/code>. Here&#8217;s an example with images in a <code>foo<\/code> module&#8217;s media directory:<\/p>\n<pre><code>import os\r\nimport glob\r\nfrom py2exe.build_exe import py2exe as build_exe\r\n\r\nclass MediaCollector(build_exe):\r\n    def copy_extensions(self, extensions):\r\n        super(MediaCollector, self).copy_extensions(extensions)\r\n\r\n        # Create the media subdir where the\r\n        # Python files are collected.\r\n        media = os.path.join('foo', 'media')\r\n        full = os.path.join(self.collect_dir, media)\r\n        if not os.path.exists(full):\r\n            self.mkpath(full)\r\n\r\n        # Copy the media files to the collection dir.\r\n        # Also add the copied file to the list of compiled\r\n        # files so it will be included in zipfile.\r\n        for f in glob.glob('foo\/media\/*'):\r\n            name = os.path.basename(f)\r\n            self.copy_file(f, os.path.join(full, name))\r\n            self.compiled_files.append(os.path.join(media, name))<\/code><\/pre>\n<p>Strictly speaking, resources are not compiled files but treating them as such was the only way I could find to force them to be included in the zipfile. To use this new collector, just specify it in your py2exe options:<\/p>\n<pre><code>py2exe_options = {\r\n    'cmdclass': {'py2exe': MediaCollector},\r\n    # [...] Other py2exe options here.\r\n}\r\n\r\nsetup(\r\n    # [...] Other setup options here.\r\n    **py2exe_options\r\n)<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In creating a Windows PDF generation app for a client of mine, I ran into a limitation of <a href=\"http:\/\/py2exe.org\">py2exe<\/a> which prevents resource loading with <a href=\"http:\/\/peak.telecommunity.com\/DevCenter\/PkgResources\">pkg_resources<\/a>. The module attempts to load resources from py2exe&#8217;s library.zip. As the zipfile is reserved for compiled bytecode, there&#8217;s no option to copy files into it. You can copy&nbsp;&hellip;&nbsp;<a class=\"read_more\" href=\"http:\/\/crazedmonkey.com\/blog\/python\/pkg_resources-with-py2exe.html\" rel=\"bookmark\" title=\"Continue reading &ldquo;Extend py2exe to copy files to the zipfile where pkg_resources can load them&rdquo;\"><span>Read more<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[29],"tags":[30,876],"_links":{"self":[{"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/posts\/380"}],"collection":[{"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/comments?post=380"}],"version-history":[{"count":1,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/posts\/380\/revisions"}],"predecessor-version":[{"id":381,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/posts\/380\/revisions\/381"}],"wp:attachment":[{"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/media?parent=380"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/categories?post=380"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/crazedmonkey.com\/blog\/wp-json\/wp\/v2\/tags?post=380"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}