From 1302d476579eae6df589c1d319f84c9247d1199c Mon Sep 17 00:00:00 2001 From: Scott Torborg Date: Sat, 17 Nov 2012 15:02:59 -0800 Subject: [PATCH] initial commit, basic structure in place, but additions are still a WIP --- .gitignore | 2 + Makefile | 153 ++++++++++++++++++++++++ about.rst | 12 ++ command-line-scripts.rst | 10 ++ conf.py | 244 +++++++++++++++++++++++++++++++++++++++ dependencies.rst | 74 ++++++++++++ documentation.rst | 6 + everything.rst | 6 + index.rst | 38 ++++++ metadata.rst | 87 ++++++++++++++ minimal.rst | 163 ++++++++++++++++++++++++++ non-code-files.rst | 6 + testing.rst | 6 + 13 files changed, 807 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 about.rst create mode 100644 command-line-scripts.rst create mode 100644 conf.py create mode 100644 dependencies.rst create mode 100644 documentation.rst create mode 100644 everything.rst create mode 100644 index.rst create mode 100644 metadata.rst create mode 100644 minimal.rst create mode 100644 non-code-files.rst create mode 100644 testing.rst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f626bdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Rendered documentation. +_build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38b91a1 --- /dev/null +++ b/Makefile @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-packaging.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-packaging.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/python-packaging" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python-packaging" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/about.rst b/about.rst new file mode 100644 index 0000000..764fc0f --- /dev/null +++ b/about.rst @@ -0,0 +1,12 @@ +About This Tutorial / Contributing +---------------------------------- + +I wrote this tutorial in an attempt to improve the state of Python packages at large. Tools like `virtualenv `_ and `pip `_, as well as improvements to setuptools, have made + +This documentation is written in reStructuredText and built with `Sphinx `_. The source is open and hosted at http://github.com/storborg/python-packaging. + +To build the HTML version, just do this in a clone of the repo:: + + $ make html + +Contributions and fixes are welcome, encouraged, and credited. Please submit a pull request on GitHub or email me: `storborg@gmail.com `_. diff --git a/command-line-scripts.rst b/command-line-scripts.rst new file mode 100644 index 0000000..ab17e9d --- /dev/null +++ b/command-line-scripts.rst @@ -0,0 +1,10 @@ +Command Line Scripts +-------------------- + +.. todo:: + + Explain how to use setuptools console_scripts entrypoints. This is preferred for testability. + +.. todo:: + + Explain how to use the setup() scripts kwarg. diff --git a/conf.py b/conf.py new file mode 100644 index 0000000..b755ff5 --- /dev/null +++ b/conf.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# +# python-packaging documentation build configuration file, created by +# sphinx-quickstart on Sat Nov 17 10:03:55 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.todo'] + +todo_include_todos = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'python-packaging' +copyright = u'2012, Scott Torborg' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = 'How To Package Your Python Code' + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'python-packagingdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'python-packaging.tex', u'python-packaging Documentation', + u'Scott Torborg', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'python-packaging', u'python-packaging Documentation', + [u'Scott Torborg'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'python-packaging', u'python-packaging Documentation', + u'Scott Torborg', 'python-packaging', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff --git a/dependencies.rst b/dependencies.rst new file mode 100644 index 0000000..609f4b1 --- /dev/null +++ b/dependencies.rst @@ -0,0 +1,74 @@ +Specifying Dependencies +----------------------- + +If you're using Python, odds are you're going to want to use other public packages from PyPI or elsewhere. + +Fortunately, setuptools makes it easy for us to specify those dependencies (assuming they are packaged correctly) and automatically install them when our packages is installed. + +We can add some formatting spice to the **funniest** joke with `Markdown `_. + +In ``__init__.py``:: + + from markdown import markdown + + def joke(): + return markdown(u'Wenn ist das Nunst\u00fcck git und Slotermeyer?' + u'Ja! ... **Beiherhund** das Oder die Flipperwaldt ' + u'gersput.') + +Now our package depends on the ``markdown`` package. To note that in ``setup.py``, we just add an ``install_requires`` keyword argument:: + + from setuptools import setup + + setup(name='funniest', + version='0.1', + description='The funniest joke in the world', + url='http://github.com/storborg/funniest', + author='Flying Circus', + author_email='flyingcircus@example.com', + license='MIT', + packages=['funniest'], + install_requires=[ + 'markdown', + ], + zip_safe=False) + +To prove this works, we can run ``python setup.py develop`` again, and we'll see:: + + $ python setup.py develop + running develop + running egg_info + writing requirements to funniest.egg-info/requires.txt + writing funniest.egg-info/PKG-INFO + writing top-level names to funniest.egg-info/top_level.txt + writing dependency_links to funniest.egg-info/dependency_links.txt + reading manifest file 'funniest.egg-info/SOURCES.txt' + writing manifest file 'funniest.egg-info/SOURCES.txt' + running build_ext + Creating /.../site-packages/funniest.egg-link (link to .) + funniest 0.1 is already the active version in easy-install.pth + + Installed /Users/scott/local/funniest + Processing dependencies for funniest==0.1 + Searching for Markdown==2.1.1 + Best match: Markdown 2.1.1 + Adding Markdown 2.1.1 to easy-install.pth file + + Using /.../site-packages + Finished processing dependencies for funniest==0.1 + +When we publish this to PyPI, calling ``pip install funniest`` or similar will also install ``markdown``. + + +Packages Not On PyPI +~~~~~~~~~~~~~~~~~~~~ + +Sometimes you'll want to use packages that are properly arranged with setuptools, but aren't published to PyPI. In those cases, you can specify a list of one or more ``dependency_links`` URLs where the package can be downloaded, along with some additional hints, and setuptools will find and install the package correctly. + +For example, if a library is published on GitHub, you can specify it like:: + + setup( + ... + dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0'] + ... + ) diff --git a/documentation.rst b/documentation.rst new file mode 100644 index 0000000..3f4a096 --- /dev/null +++ b/documentation.rst @@ -0,0 +1,6 @@ +Documentation with Sphinx +------------------------- + +.. todo:: + + Explain how to set up and use Sphinx. diff --git a/everything.rst b/everything.rst new file mode 100644 index 0000000..1be97be --- /dev/null +++ b/everything.rst @@ -0,0 +1,6 @@ +Putting It All Together +----------------------- + +.. todo:: + + Show entire repo with all integrations, e.g. ready to clone for direct use. diff --git a/index.rst b/index.rst new file mode 100644 index 0000000..6ed6dac --- /dev/null +++ b/index.rst @@ -0,0 +1,38 @@ +How To Package Your Python Code +=============================== + +This tutorial aims to clarify the current state of Python packaging, and put forth an opinionated and specific pattern to make trouble-free packages for community use. The documentation herein doesn't describe the *only* way of doing things, merely one specific approach that works well. + +In particular, those packages should make it easy: + +* To install with ``pip`` or ``easy_install``. +* To specify as a dependency for another package. +* For other users to download and run tests. +* For other users to work on and have immediate familiary with the basic directory strucuture. +* To add and distribute documentation. + +We'll walk through the basic steps of building up a contrived package **funniest** to support these things. + +.. toctree:: + :maxdepth: 1 + + minimal + dependencies + metadata + testing + command-line-scripts + non-code-files + documentation + everything + about + +.. note:: + + At this time, this documentation focuses on Python 2.x only, and may not be *as* applicable to packages targeted to Python 3.x. + + +Indices +======= + +* :ref:`genindex` +* :ref:`search` diff --git a/metadata.rst b/metadata.rst new file mode 100644 index 0000000..b4bbd83 --- /dev/null +++ b/metadata.rst @@ -0,0 +1,87 @@ +Better Package Metadata +----------------------- + +The ``setuptools.setup()`` call accepts a variety of keyword arguments to specify additional metadata about your package. This can help people find your package and evaluate quickly whether or not it is what they're looking for.:: + + from setuptools import setup + + setup(name='funniest', + version='0.1', + description='The funniest joke in the world', + long_description='Really, the funniest around.', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2.7', + 'Topic :: Text Processing :: Linguistic', + ], + keywords='funniest joke comedy flying circus', + url='http://github.com/storborg/funniest', + author='Flying Circus', + author_email='flyingcircus@example.com', + license='MIT', + packages=['funniest'], + install_requires=[ + 'markdown', + ], + zip_safe=False) + +For a full list of the possible arguments to ``classifiers``, visit http://pypi.python.org/pypi?%3Aaction=list_classifiers. + + +A README / Long Description +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You'll probably want a README file in your source distribution, and that file can serve double purpose as the ``long_description`` specified to PyPI. Further, if that file is written in reStructuredText, it can be formatted nicely. + +For **funniest**, let's add two files:: + + funniest/ + funniest/ + __init__.py + setup.py + README.rst + MANIFEST.in + +``README.rst`` contains:: + + Funniest + -------- + + To use (with caution), simply do:: + + >>> import funniest + >>> print funniest.joke() + +``MANIFEST.in`` contains:: + + include README.rst + +This file is necessary to tell setuptools to include the README.rst file when generating source distributions. Otherwise, only Python files will be included. + +Now we can use it in setup.py like:: + + from setuptools import setup + + setup(name='funniest', + version='0.1', + description='The funniest joke in the world', + long_description=open('README.rst').read(), + classifiers=[ + 'Development Status :: 3 - Alpha', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 2.7', + 'Topic :: Text Processing :: Linguistic', + ], + keywords='funniest joke comedy flying circus', + url='http://github.com/storborg/funniest', + author='Flying Circus', + author_email='flyingcircus@example.com', + license='MIT', + packages=['funniest'], + install_requires=[ + 'markdown', + ], + zip_safe=False) + +When the repo is hosted on GitHub or BitBucket, the README.rst file will also automatically be picked up and used as a 'homepage' for the project. diff --git a/minimal.rst b/minimal.rst new file mode 100644 index 0000000..9b12670 --- /dev/null +++ b/minimal.rst @@ -0,0 +1,163 @@ +Minimal Structure +----------------- + +We'll start with some Python code. Native German speakers, `please proceed with caution `_:: + + def joke(): + return (u'Wenn ist das Nunst\u00fcck git und Slotermeyer? Ja! ... ' + u'Beiherhund das Oder die Flipperwaldt gersput.') + +The beauty and elegance of this implementation simply demands that it be packaged properly for distribution. + + +Picking A Name +~~~~~~~~~~~~~~ + +Python module/package names should generally follow the following constraints: + +* All lowercase +* Unique on pypi, even if you don't want to make your package publicly available (you might want to specify it privately as a dependency later) +* Underscore-separated or no word seperators at all (don't use hyphens) + +We've decided to turn our bit of code into a module called **funniest**. + + +Creating The Scaffolding +~~~~~~~~~~~~~~~~~~~~~~~~ + +The initial directory structure for **funniest** should look like this:: + + funniest/ + funniest/ + __init__.py + setup.py + +The top level directory is the root of our SCM repo, e.g. ``funniest.git``. The subdir, also called ``funniest``, is the actual Python module. + +For starters we'll put the ``joke()`` function in ``__init__.py``, so it just contains:: + + def joke(): + return (u'Wenn ist das Nunst\u00fcck git und Slotermeyer? Ja! ... ' + u'Beiherhund das Oder die Flipperwaldt gersput.') + +The main setup config file, ``setup.py``, should contain a single call to ``setuptools.setup()``, like so:: + + from setuptools import setup + + setup(name='funniest', + version='0.1', + description='The funniest joke in the world', + url='http://github.com/storborg/funniest', + author='Flying Circus', + author_email='flyingcircus@example.com', + license='MIT', + packages=['funniest'], + zip_safe=False) + +Now we can install the package locally (for use on our system), with:: + + python setup.py install + +We can also install the package with a symlink, so that changes to the source files will be immediately available to other users of the package on our system:: + + python setup.py develop + +Anywhere else in our system using the same Python, we can do this now:: + + >>> import funniest + >>> print funniest.joke() + + +Publishing On PyPI +~~~~~~~~~~~~~~~~~~ + +The ``setup.py`` script is also our main entrypoint to register the package name on PyPI and upload source distributions. + +To "register" the package (this will reserve the name, upload package metadata, and create the pypi.python.org webpage):: + + python setup.py register + +If you haven't published things on PyPI before, you'll need to create an account by following the steps provided at this point. + +At this point you can view the (very minimal) page on PyPI describing **funniest**: + +http://pypi.python.org/pypi/funniest/0.1 + +Although users can follow the URL link to find our git repository, we'll probably want to upload a source distribution so that the package can be installed without cloning the repository. This will also enable automated installation and dependency resolution tools to install our package. + +First create a source distribution with:: + + python setup.py sdist + +This will create ``dist/funniest-0.1.tar.gz`` inside our top-level directory. If you like, copy that file to another host and try unpacking it and install it, just to verify that it works for you. + +That file can then be uploaded to PyPI with:: + + python setup.py upload + +You can combine all of these steps, to update metadata and publish a new build in a single step:: + + python setup.py register sdist upload + + +Installing the Package +~~~~~~~~~~~~~~~~~~~~~~ + +At this point, other consumers of this package can install the package with ``easy_install``:: + + easy_install funniest + +Or better yet, ``pip``: + + pip install funniest + +They can specify it as a dependency for another package, and it will be automatically installed when that package is installed (we'll get to how to do that later). + + +Adding Additional Files +~~~~~~~~~~~~~~~~~~~~~~~ + +Most of the time we'll want more than one file containing code inside of our module. Additional files should always be added inside the inner ``funniest`` directory. + +For example, let's move our one function to a new ``text`` submodule, so our directory hierarchy looks like this:: + + funniest/ + funniest/ + __init__.py + text.py + setup.py + +In ``__init__.py``:: + + from .text import joke + +In ``joke.py``:: + + def joke(): + return (u'Wenn ist das Nunst\u00fcck git und Slotermeyer? Ja! ... ' + u'Beiherhund das Oder die Flipperwaldt gersput.') + +All additional Python code belongs in the ``funniest/funnest/`` directory. + + +Ignoring Files (.gitignore, etc) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There's one more thing we'll probably want in a 'bare bones' package: a ``.gitignore`` file, or the equivalent for other SCMs. The Python build system creates a number of intermediary files we'll want to be careful to not commit to source control. Here's an example of what ``.gitignore`` should look like for **funniest**:: + + # Compiled python modules. + *.pyc + + # Setuptools distribution folder. + /dist/ + + # Python egg metadata, regenerated from source files by setuptools. + /maitai.egg-info + + +That's All You Need +~~~~~~~~~~~~~~~~~~~ + +The structure described so far is all that's necessary to create reusable simple packages with no 'packaging bugs'. If every published Python tool or library used followed these rules, the world would be a better place. + +**But wait, there's more!** Most packages will want to add things like command line scripts, documentation, tests, and analysis tools. Read on for more. diff --git a/non-code-files.rst b/non-code-files.rst new file mode 100644 index 0000000..df4f2fd --- /dev/null +++ b/non-code-files.rst @@ -0,0 +1,6 @@ +Adding Non-Code Files +--------------------- + +.. todo:: + + Explain ``MANIFEST.in`` and basics of adding things like images, data files, etc. diff --git a/testing.rst b/testing.rst new file mode 100644 index 0000000..eb6ec40 --- /dev/null +++ b/testing.rst @@ -0,0 +1,6 @@ +Let There Be Tests +------------------ + +.. todo:: + + Explain basics of configuring nose, requiring it, and using the setup.py integration.