summaryrefslogtreecommitdiff
path: root/setup.py
diff options
context:
space:
mode:
Diffstat (limited to 'setup.py')
-rw-r--r--setup.py269
1 files changed, 232 insertions, 37 deletions
diff --git a/setup.py b/setup.py
index 548a923..54780e0 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,42 @@ This script supports publishing Pystache to PyPI.
This docstring contains instructions to Pystache maintainers on how
to release a new version of Pystache.
-(1) Push to PyPI. To release a new version of Pystache to PyPI--
+(1) Prepare the release.
+
+Make sure the code is finalized and merged to master. Bump the version
+number in setup.py, etc.
+
+Generate the reStructuredText long_description using--
+
+ $ python setup.py prep
+
+and be sure this new version is checked in. You must have pandoc installed
+to do this step:
+
+ http://johnmacfarlane.net/pandoc/
+
+It helps to review this auto-generated file on GitHub prior to uploading
+because the long description will be sent to PyPI and appear there after
+publishing. PyPI attempts to convert this string to HTML before displaying
+it on the PyPI project page. If PyPI finds any issues, it will render it
+instead as plain-text, which we do not want.
+
+To check in advance that PyPI will accept and parse the reST file as HTML,
+you can use the rst2html program installed by the docutils package
+(http://docutils.sourceforge.net/). To install docutils:
+
+ $ pip install docutils
+
+To check the file, run the following command and confirm that it reports
+no warnings:
+
+ $ python setup.py --long-description | rst2html.py -v --no-raw > out.html
+
+See here for more information:
+
+ http://docs.python.org/distutils/uploading.html#pypi-package-display
+
+(2) Push to PyPI. To release a new version of Pystache to PyPI--
http://pypi.python.org/pypi/pystache
@@ -15,8 +50,7 @@ create a PyPI user account if you do not already have one. The user account
will need permissions to push to PyPI. A current "Package Index Owner" of
Pystache can grant you those permissions.
-When you have permissions, run the following (after preparing the release,
-merging to master, bumping the version number in setup.py, etc):
+When you have permissions, run the following:
python setup.py publish
@@ -35,7 +69,7 @@ as described here, for example:
http://docs.python.org/release/2.5.2/dist/pypirc.html
-(2) Tag the release on GitHub. Here are some commands for tagging.
+(3) Tag the release on GitHub. Here are some commands for tagging.
List current tags:
@@ -52,12 +86,22 @@ Push a tag to GitHub:
"""
import os
+import shutil
import sys
+
py_version = sys.version_info
-# Distribute works with Python 2.3.5 and above:
+# distutils does not seem to support the following setup() arguments.
+# It displays a UserWarning when setup() is passed those options:
+#
+# * entry_points
+# * install_requires
+#
+# distribute works with Python 2.3.5 and above:
+#
# http://packages.python.org/distribute/setuptools.html#building-and-distributing-packages-with-distribute
+#
if py_version < (2, 3, 5):
# TODO: this might not work yet.
import distutils as dist
@@ -67,16 +111,20 @@ else:
import setuptools as dist
setup = dist.setup
-# TODO: use the logging module instead of printing.
-# TODO: include the following in a verbose mode.
-# print("Using: version %s of %s" % (repr(dist.__version__), repr(dist)))
+VERSION = '0.5.3-rc' # Also change in pystache/__init__.py.
-VERSION = '0.5.2' # Also change in pystache/__init__.py.
+FILE_ENCODING = 'utf-8'
-HISTORY_PATH = 'HISTORY.rst'
+README_PATH = 'README.md'
+HISTORY_PATH = 'HISTORY.md'
LICENSE_PATH = 'LICENSE'
-README_PATH = 'README.rst'
+
+RST_DESCRIPTION_PATH = 'setup_description.rst'
+
+TEMP_EXTENSION = '.temp'
+
+PREP_COMMAND = 'prep'
CLASSIFIERS = (
'Development Status :: 4 - Beta',
@@ -90,8 +138,15 @@ CLASSIFIERS = (
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: Implementation :: PyPy',
)
+# Comments in reST begin with two dots.
+RST_LONG_DESCRIPTION_INTRO = """\
+.. Do not edit this file. This file is auto-generated for PyPI by setup.py
+.. using pandoc, so edits should go in the source files rather than here.
+"""
+
def read(path):
"""
@@ -106,49 +161,151 @@ def read(path):
finally:
f.close()
- return b.decode('utf-8')
+ return b.decode(FILE_ENCODING)
-def publish():
+def write(u, path):
"""
- Publish this package to PyPI (aka "the Cheeseshop").
+ Write a unicode string to a file (as utf-8).
"""
- os.system('python setup.py sdist upload')
+ print("writing to: %s" % path)
+ # This function implementation was chosen to be compatible across Python 2/3.
+ f = open(path, "wb")
+ try:
+ b = u.encode(FILE_ENCODING)
+ f.write(b)
+ finally:
+ f.close()
+
+
+def make_temp_path(path, new_ext=None):
+ """
+ Arguments:
+
+ new_ext: the new file extension, including the leading dot.
+ Defaults to preserving the existing file extension.
+
+ """
+ root, ext = os.path.splitext(path)
+ if new_ext is None:
+ new_ext = ext
+ temp_path = root + TEMP_EXTENSION + new_ext
+ return temp_path
+
+
+def strip_html_comments(text):
+ """Strip HTML comments from a unicode string."""
+ lines = text.splitlines(True) # preserve line endings.
+
+ # Remove HTML comments (which we only allow to take a special form).
+ new_lines = filter(lambda line: not line.startswith("<!--"), lines)
+
+ return "".join(new_lines)
+
+
+# We write the converted file to a temp file to simplify debugging and
+# to avoid removing a valid pre-existing file on failure.
+def convert_md_to_rst(md_path, rst_temp_path):
+ """
+ Convert the contents of a file from Markdown to reStructuredText.
+
+ Returns the converted text as a Unicode string.
+
+ Arguments:
+
+ md_path: a path to a UTF-8 encoded Markdown file to convert.
+
+ rst_temp_path: a temporary path to which to write the converted contents.
+
+ """
+ # Pandoc uses the UTF-8 character encoding for both input and output.
+ command = "pandoc --write=rst --output=%s %s" % (rst_temp_path, md_path)
+ print("converting with pandoc: %s to %s\n-->%s" % (md_path, rst_temp_path,
+ command))
+
+ if os.path.exists(rst_temp_path):
+ os.remove(rst_temp_path)
+
+ os.system(command)
+
+ if not os.path.exists(rst_temp_path):
+ s = ("Error running: %s\n"
+ " Did you install pandoc per the %s docstring?" % (command,
+ __file__))
+ sys.exit(s)
+
+ return read(rst_temp_path)
+# The long_description needs to be formatted as reStructuredText.
+# See the following for more information:
+#
+# http://docs.python.org/distutils/setupscript.html#additional-meta-data
+# http://docs.python.org/distutils/uploading.html#pypi-package-display
+#
def make_long_description():
"""
- Return the long description for the package.
+ Generate the reST long_description for setup() from source files.
+
+ Returns the generated long_description as a unicode string.
"""
- license = """\
+ readme_path = README_PATH
+
+ # Remove our HTML comments because PyPI does not allow it.
+ # See the setup.py docstring for more info on this.
+ readme_md = strip_html_comments(read(readme_path))
+ history_md = strip_html_comments(read(HISTORY_PATH))
+ license_md = """\
License
=======
""" + read(LICENSE_PATH)
- sections = [read(README_PATH), read(HISTORY_PATH), license]
- return '\n\n'.join(sections)
+ sections = [readme_md, history_md, license_md]
+ md_description = '\n\n'.join(sections)
+ # Write the combined Markdown file to a temp path.
+ md_ext = os.path.splitext(readme_path)[1]
+ md_description_path = make_temp_path(RST_DESCRIPTION_PATH, new_ext=md_ext)
+ write(md_description, md_description_path)
-if sys.argv[-1] == 'publish':
- publish()
- sys.exit()
+ rst_temp_path = make_temp_path(RST_DESCRIPTION_PATH)
+ long_description = convert_md_to_rst(md_path=md_description_path,
+ rst_temp_path=rst_temp_path)
+
+ return "\n".join([RST_LONG_DESCRIPTION_INTRO, long_description])
+
+
+def prep():
+ """Update the reST long_description file."""
+ long_description = make_long_description()
+ write(long_description, RST_DESCRIPTION_PATH)
+
+
+def publish():
+ """Publish this package to PyPI (aka "the Cheeseshop")."""
+ long_description = make_long_description()
+
+ if long_description != read(RST_DESCRIPTION_PATH):
+ print("""\
+Description file not up-to-date: %s
+Run the following command and commit the changes--
+
+ python setup.py %s
+""" % (RST_DESCRIPTION_PATH, PREP_COMMAND))
+ sys.exit()
+
+ print("Description up-to-date: %s" % RST_DESCRIPTION_PATH)
+
+ answer = raw_input("Are you sure you want to publish to PyPI (yes/no)?")
+
+ if answer != "yes":
+ exit("Aborted: nothing published")
+
+ os.system('python setup.py sdist upload')
-# We follow the guidance here for compatibility with using setuptools instead
-# of Distribute under Python 2 (on the subject of new, unrecognized keyword
-# arguments to setup()):
-#
-# http://packages.python.org/distribute/python3.html#note-on-compatibility-with-setuptools
-#
-if py_version < (3, ):
- extra = {}
-else:
- extra = {
- # Causes 2to3 to be run during the build step.
- 'use_2to3': True,
- }
# We use the package simplejson for older Python versions since Python
# does not contain the module json before 2.6:
@@ -180,10 +337,47 @@ PACKAGES = [
]
+# The purpose of this function is to follow the guidance suggested here:
+#
+# http://packages.python.org/distribute/python3.html#note-on-compatibility-with-setuptools
+#
+# The guidance is for better compatibility when using setuptools (e.g. with
+# earlier versions of Python 2) instead of Distribute, because of new
+# keyword arguments to setup() that setuptools may not recognize.
+def get_extra_args():
+ """
+ Return a dictionary of extra args to pass to setup().
+
+ """
+ extra = {}
+ # TODO: it might be more correct to check whether we are using
+ # Distribute instead of setuptools, since use_2to3 doesn't take
+ # effect when using Python 2, even when using Distribute.
+ if py_version >= (3, ):
+ # Causes 2to3 to be run during the build step.
+ extra['use_2to3'] = True
+
+ return extra
+
+
def main(sys_argv):
- long_description = make_long_description()
+ # TODO: use the logging module instead of printing.
+ # TODO: include the following in a verbose mode.
+ print("pystache: using: version %s of %s" % (repr(dist.__version__), repr(dist)))
+
+ command = sys_argv[-1]
+
+ if command == 'publish':
+ publish()
+ sys.exit()
+ elif command == PREP_COMMAND:
+ prep()
+ sys.exit()
+
+ long_description = read(RST_DESCRIPTION_PATH)
template_files = ['*.mustache', '*.txt']
+ extra_args = get_extra_args()
setup(name='pystache',
version=VERSION,
@@ -193,6 +387,7 @@ def main(sys_argv):
author='Chris Wanstrath',
author_email='chris@ozmm.org',
maintainer='Chris Jerdonek',
+ maintainer_email='chris.jerdonek@gmail.com',
url='http://github.com/defunkt/pystache',
install_requires=INSTALL_REQUIRES,
packages=PACKAGES,
@@ -209,7 +404,7 @@ def main(sys_argv):
],
},
classifiers = CLASSIFIERS,
- **extra
+ **extra_args
)