I've finally made a foray into Python packaging;
publishing my own static site
generator. What follows are my notes on the process and some implications on
the development of what used to static
.
pip install quiescent
The first point to address is: "What is quiescent?", and the answer is simply,
a rename of my static site generator that I've
covered previously. The functionality is
largely unchanged, but the interface is better. Where previously I took the
lazy route and invoked the site generator through an if __name__ == '__main__'
block, the new package has an honest to goodness command line
interface that does the same work.
The real big news though, with my finally getting the project up on PyPI and configured correctly is that the following just works now:
pip install quiescent
cd idle-cycles/
quiescent # just like that, blog is regenerated
Why the rename? Well, it turns out every package on PyPI has to be unique, and perhaps unsurprisingly, the name static just isn't that unique. I fully admit the name quiescent isn't very good, but I sort of hate wasting cycles on this kind of thing.
Much of the work involved was documented (pretty well) in
this guide,
with a few minor changes or differences. In order to make my own test
configuration work with setup.py
I first stripped out another if __name__ == '__main__'
block that guarded test execution. With the following configuration
in setup.py
, tests may be invoked with the (I guess recommended?) interface:
setup(name="quiescent",
...
version="0.1",
description="A static weblog generator",
long_description=readme(),
packages=['quiescent'],
python_requires='python>=3.6',
install_requires=[
'Jinja2 >= 2.8',
'MarkupSafe >= 0.23',
'mistune >= 0.7.3',
],
test_suite='quiescent.tests',
entry_points={
'console_scripts': ['quiescent=quiescent.command_line:main']
},
zip_safe=False)
I am slowly coming around to the standard configuration of Python projects, which involves moving my tests into their own directory, so that imports don't clutter the package namespace. Similarly, while it seems odd to do so, I've nested a quiescent directory inside of the top-level directory of the same name.
One bit that I was very happy to learn and finally get right was the console scripts directive, which specifies command-line programs executable within the package. It is eminently nicer to finally have a single command-line program named sensibly and properly configured.
One small hiccup in the process of uploading the package to PyPI was in an
opaque error after attempting to upload. It seems the only way to provide a
username with which to use when uploading is through a .pypirc
file in the
home directory; not really a problem, but a bit underdocumented. The fix for me
was to include the following in the file:
[distutils]
index-servers = pypi
[pypi]
username: nprescott
I am not certain I have the python_requires
directive correct yet. The idea
is to limit the potential installations to only those that are specified (in my
case, only Python 3.6 due to the use of f
-strings). With limited testing, it
seems I am still able to install into a Python 2 environment, I'll need to dig
deeper and see if my installation of pip
is at least version 9. In an attempt
to test the above, I ended up bumping the version number (to 0.2), because of
difficulty testing the final packagea prior to uploading. It seems the thing to
do is use twine, which is designed to avoid
this exact problem.
Finally, in between rewriting the README, patching in the new "bootstrap" function, and repackaging the tests; I realized -- some of the architecture in the current version is less than stellar. Running coverage reveals large portions of the code are not yet tested, specifically those that serve to write file output or create and move directories. I've been mulling over how to better design these necessary I/O dependencies and am considering a more "functional core, imperative shell" model, which I have yet to try.
The point of which seems to be, actually publishing something where people might see it in a state beyond "it's code you found on Github, what more do you want" sort of forces better decision making. A problem for another day.