mirror of
https://github.com/kennethreitz/python-guide.git
synced 2026-06-05 23:00:18 +00:00
Structure of the Repository
This commit is contained in:
+291
-2
@@ -19,8 +19,297 @@ project. We then discuss various perspectives on how to build code which
|
||||
can be extended and tested reliably.
|
||||
|
||||
|
||||
Structure is Key
|
||||
----------------
|
||||
|
||||
Structure of the Repository
|
||||
---------------------------
|
||||
|
||||
It's Important.
|
||||
:::::::::::::::
|
||||
|
||||
Just as Code Style, API Design, and Automation are essential for a
|
||||
healthy development cycle, Repository structure is a crucial part of
|
||||
your project's
|
||||
`architecture <http://www.amazon.com/gp/product/1257638017/ref=as_li_ss_tl?ie=UTF8&tag=bookforkind-20&linkCode=as2&camp=1789&creative=39095&creativeASIN=1257638017>`__.
|
||||
|
||||
When a potential user or contributor lands on your repository's page,
|
||||
they see a few things:
|
||||
|
||||
- Project Name
|
||||
- Project Description
|
||||
- Bunch O' Files
|
||||
|
||||
Only when they scroll below the fold will the user see your project's
|
||||
README.
|
||||
|
||||
If your repo is a massive dump of files or a nested mess of directories,
|
||||
they might look elsewhere before even reading your beautiful
|
||||
documentation.
|
||||
|
||||
Dress for the job you want, not the job you have.
|
||||
|
||||
Of course, first impressions aren't everything. You and your colleagues
|
||||
will spend countless hours working with this repository, eventually
|
||||
becoming intimately familiar with every nook and cranny. The layout of
|
||||
it is important.
|
||||
|
||||
Sample Repository
|
||||
:::::::::::::::::
|
||||
|
||||
**tl;dr**: This is what `Kenneth Reitz <http://kennethreitz.org>`_ recommends.
|
||||
|
||||
This repository is `available on
|
||||
GitHub <https://github.com/kennethreitz/samplemod>`__.
|
||||
|
||||
::
|
||||
|
||||
README.rst
|
||||
LICENSE
|
||||
setup.py
|
||||
requirements.txt
|
||||
sample/__init__.py
|
||||
sample/core.py
|
||||
sample/helpers.py
|
||||
docs/conf.py
|
||||
docs/index.rst
|
||||
tests/test_basic.py
|
||||
tests/test_advanced.py
|
||||
|
||||
Let's get into some specifics.
|
||||
|
||||
The Actual Module
|
||||
:::::::::::::::::
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./sample/`` or ``./sample.py``"
|
||||
"Purpose", "The code of interest"
|
||||
|
||||
|
||||
Your module package is the core focus of the repository. It should not
|
||||
be tucked away:
|
||||
|
||||
::
|
||||
|
||||
./sample/
|
||||
|
||||
If your module consists of only a single file, you can place it directly
|
||||
in the root of your repository:
|
||||
|
||||
::
|
||||
|
||||
./sample.py
|
||||
|
||||
Your library does not belong in an ambiguous src or python subdirectory.
|
||||
|
||||
License
|
||||
:::::::
|
||||
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./LICENSE``"
|
||||
"Purpose", "Lawyering up."
|
||||
|
||||
|
||||
This is arguably the most important part of your repository, aside from
|
||||
the source code itself. The full license text and copyright claims
|
||||
should exist in this file.
|
||||
|
||||
If you aren't sure which license you should use for your project, check
|
||||
out `choosealicense.com <http://choosealicense.com>`_.
|
||||
|
||||
Of course, you are also free to publish code without a license, but this
|
||||
would prevent many people from potentially using your code.
|
||||
|
||||
Setup.py
|
||||
::::::::
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./setup.py``"
|
||||
"Purpose", "Package and distribution management."
|
||||
|
||||
|
||||
If your module package is at the root of your repository, this should
|
||||
obviously be at the root as well.
|
||||
|
||||
Requirements File
|
||||
:::::::::::::::::
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./requirements.txt``"
|
||||
"Purpose", "Development dependencies."
|
||||
|
||||
|
||||
A `pip requirements
|
||||
file <http://www.pip-installer.org/en/latest/requirements.html>`__
|
||||
should be placed at the root of the repository. It should specify the
|
||||
dependencies required to contribute to the project: testing, building,
|
||||
and generating documentation.
|
||||
|
||||
If your project has no development dependencies, or you prefer
|
||||
development environment setup via ``setup.py``, this file may be
|
||||
unnecessary.
|
||||
|
||||
Documentation
|
||||
:::::::::::::
|
||||
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./docs/``"
|
||||
"Purpose", "Package reference documentation."
|
||||
|
||||
There is little reason for this to exist elsewhere.
|
||||
|
||||
Test Suite
|
||||
::::::::::
|
||||
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./test_sample.py`` or ``./tests``"
|
||||
"Purpose", "Package integration and unit tests."
|
||||
|
||||
Starting out, a small test suite will often exist in a single file:
|
||||
|
||||
::
|
||||
|
||||
./test_sample.py
|
||||
|
||||
Once a test suite grows, you can move your tests to a directory, like
|
||||
so:
|
||||
|
||||
::
|
||||
|
||||
tests/test_basic.py
|
||||
tests/test_advanced.py
|
||||
|
||||
Obviously, these test modules must import your packaged module to test
|
||||
it. You can do this a few ways:
|
||||
|
||||
- Expect the package to be installed in site-packages.
|
||||
- Use a simple (but *explicit*) path modification to resolve the
|
||||
package properly.
|
||||
|
||||
I highly recommend the latter. Requiring a developer to run
|
||||
`setup.py <http://setup.py>`__ develop to test an actively changing
|
||||
codebase also requires them to have an isolated environment setup for
|
||||
each instance of the codebase.
|
||||
|
||||
To give the individual tests import context, create a tests/context.py
|
||||
file:
|
||||
|
||||
::
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
import sample
|
||||
|
||||
Then, within the individual test modules, import the module like so:
|
||||
|
||||
::
|
||||
|
||||
from .context import sample
|
||||
|
||||
This will always work as expected, regardless of installation method.
|
||||
|
||||
Some people will assert that you should distribute your tests within
|
||||
your module itself -- I disagree. It often increases complexity for your
|
||||
users; many test suites often require additional dependencies and
|
||||
runtime contexts.
|
||||
|
||||
Makefile
|
||||
::::::::
|
||||
|
||||
|
||||
.. csv-table::
|
||||
:widths: 20, 40
|
||||
|
||||
"Location", "``./Makefile``"
|
||||
"Purpose", "Generic management tasks."
|
||||
|
||||
|
||||
If you look at most of my projects or any Pocoo project, you'll notice a
|
||||
Makefile laying around. Why? These projects aren't written in C... In
|
||||
short, make is a incredibly useful tool for defining generic tasks for
|
||||
your project.
|
||||
|
||||
**Sample Makefile:**
|
||||
|
||||
::
|
||||
|
||||
init:
|
||||
pip install -r requirements.txt
|
||||
|
||||
test:
|
||||
py.test tests
|
||||
|
||||
Other generic management scripts (e.g. ``manage.py``
|
||||
or ``fabfile.py``) belong at the root of the repository as well.
|
||||
|
||||
Regarding Django Applications
|
||||
:::::::::::::::::::::::::::::
|
||||
|
||||
I've noticed a new trend in Django applications since the release of
|
||||
Django 1.4. Many developers are structuring their repositories poorly
|
||||
due to the new bundled application templates.
|
||||
|
||||
How? Well, they go to their bare and fresh repository and run the
|
||||
following, as they always have:
|
||||
|
||||
::
|
||||
|
||||
$ django-admin.py start-project samplesite
|
||||
|
||||
The resulting repository structure looks like this:
|
||||
|
||||
::
|
||||
|
||||
README.rst
|
||||
samplesite/manage.py
|
||||
samplesite/samplesite/settings.py
|
||||
samplesite/samplesite/wsgi.py
|
||||
samplesite/samplesite/sampleapp/models.py
|
||||
|
||||
Don't do this.
|
||||
|
||||
Repetitive paths are confusing for both your tools and your developers.
|
||||
Unnecessary nesting doesn't help anybody (unless they're nostalgic for
|
||||
monolithic SVN repos).
|
||||
|
||||
Let's do it properly:
|
||||
|
||||
::
|
||||
|
||||
$ django-admin.py start-project samplesite .
|
||||
|
||||
Note the "``.``".
|
||||
|
||||
The resulting structure:
|
||||
|
||||
::
|
||||
|
||||
README.rst
|
||||
manage.py
|
||||
samplesite/settings.py
|
||||
samplesite/wsgi.py
|
||||
samplesite/sampleapp/models.py
|
||||
|
||||
|
||||
|
||||
|
||||
Structure of Code is Key
|
||||
------------------------
|
||||
|
||||
Thanks to the way imports and modules are handled in Python, it is
|
||||
relatively easy to structure a Python project. Easy, here, means
|
||||
|
||||
Reference in New Issue
Block a user