{"id":161,"date":"2013-04-02T00:17:03","date_gmt":"2013-04-02T05:17:03","guid":{"rendered":"http:\/\/homepages.uc.edu\/~yaozo\/wordpress\/?p=161"},"modified":"2013-04-02T00:17:03","modified_gmt":"2013-04-02T05:17:03","slug":"filesystem-structure-of-a-python-project","status":"publish","type":"post","link":"https:\/\/zhuoyao.net\/index.php\/2013\/04\/02\/filesystem-structure-of-a-python-project\/","title":{"rendered":"Filesystem structure of a Python project"},"content":{"rendered":"<p>Do:<\/p>\n<ul>\n<li>name the directory something related to your project. For example, if your project is named &#8220;Twisted&#8221;, name the top-level directory for its source files\u00a0<code>Twisted<\/code>. When you do releases, you should include a version number suffix:\u00a0<code>Twisted-2.5<\/code>.<\/li>\n<li>create a directory\u00a0<code>Twisted\/bin<\/code>\u00a0and put your executables there, if you have any. Don&#8217;t give them a\u00a0<code>.py<\/code>extension, even if they are Python source files. Don&#8217;t put any code in them except an import of and call to a main function defined somewhere else in your projects. (Slight wrinkle: since on Windows, the interpreter is selected by the file extension, your Windows users actually do want the .py extension. So, when you package for Windows, you may want to add it. Unfortunately there&#8217;s no easy distutils trick that I know of to automate this process. Considering that on POSIX the .py extension is a only a wart, whereas on Windows the lack is an actual bug, if your userbase includes Windows users, you may want to opt to just have the .py extension everywhere.)<\/li>\n<li>If your project is expressable as a single Python source file, then put it into the directory and name it something related to your project. For example,\u00a0<code>Twisted\/twisted.py<\/code>. If you need multiple source files, create a package instead (<code>Twisted\/twisted\/<\/code>, with an empty\u00a0<code>Twisted\/twisted\/__init__.py<\/code>) and place your source files in it. For example,\u00a0<code>Twisted\/twisted\/internet.py<\/code>.<\/li>\n<li>put your unit tests in a sub-package of your package (note &#8211; this means that the\u00a0<em>single Python source file<\/em>\u00a0option above was a trick &#8211; you\u00a0<strong>always<\/strong>\u00a0need at least one other file for your unit tests). For example,<code>Twisted\/twisted\/test\/<\/code>. Of course, make it a package with\u00a0<code>Twisted\/twisted\/test\/__init__.py<\/code>. Place tests in files like\u00a0<code>Twisted\/twisted\/test\/test_internet.py<\/code>.<\/li>\n<li>add\u00a0<code>Twisted\/README<\/code>\u00a0and\u00a0<code>Twisted\/setup.py<\/code>\u00a0to explain and install your software, respectively, if you&#8217;re feeling nice.<\/li>\n<\/ul>\n<p>Don&#8217;t:<\/p>\n<ul>\n<li>put your source in a directory called\u00a0<code>src<\/code>\u00a0or\u00a0<code>lib<\/code>. This makes it hard to run without installing.<\/li>\n<li>put your tests outside of your Python package. This makes it hard to run the tests against an installed version.<\/li>\n<li>create a package that\u00a0<strong>only<\/strong>\u00a0has a\u00a0<code>__init__.py<\/code>\u00a0and then put all your code into\u00a0<code>__init__.py<\/code>. Just make a module instead of a package, it&#8217;s simpler.<\/li>\n<li>try to come up with magical hacks to make Python able to import your module or package without having the user add the directory containing it to their import path (either via\u00a0<code>PYTHONPATH<\/code>\u00a0or some other mechanism). You will\u00a0<strong>not<\/strong>\u00a0correctly handle all cases and users will get angry at you when your software doesn&#8217;t work in their environment.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Do: name the directory something related to your project. For example, if your project is named &#8220;Twisted&#8221;, name the top-level directory for its source files\u00a0Twisted.&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19],"tags":[],"class_list":["post-161","post","type-post","status-publish","format-standard","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts\/161","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/comments?post=161"}],"version-history":[{"count":0,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/posts\/161\/revisions"}],"wp:attachment":[{"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/media?parent=161"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/categories?post=161"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zhuoyao.net\/index.php\/wp-json\/wp\/v2\/tags?post=161"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}