Detailed tests of pypi.py ========================= .. :doctest: Note on test setup: we don't use the "big" setup/teardown methods here. >>> from zest.releaser import pypi >>> import pkg_resources Parsing the configuration file ------------------------------ For pypi uploads and uploads to multiple servers, a configuration file needs to be available: >>> pypi.DIST_CONFIG_FILE '.pypirc' This is the default. For testing purposes, you *can* pass in a config file's name yourself. We'll do that in the rest of these tests. A missing file doesn't lead to an error: >>> pypiconfig = pypi.PypiConfig(config_filename='non/existing', use_setup_cfg=False) >>> pypiconfig.config is None True There are two styles of ``.pypirc`` files. The old one just for pypi: >>> pypirc_old = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_old.txt') >>> print(open(pypirc_old).read()) [server-login] username:pipo_de_clown password:asjemenou [zest.releaser] extra-message = [ci skip] >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_old) >>> pypiconfig.is_old_pypi_config() True >>> pypiconfig.is_new_pypi_config() False And the new format that allows multiple uploads: >>> pypirc_new = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_new.txt') >>> print(open(pypirc_new).read()) [distutils] index-servers = pypi plone.org mycompany [pypi] username:user password:password [plone.org] repository:http://plone.org/products username:ploneuser password:password [mycompany] repository:http://my.company/products username:user password:password [zest.releaser] extra-message = [ci skip] >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_new) >>> pypiconfig.is_old_pypi_config() False >>> pypiconfig.is_new_pypi_config() True A file with both is also possible: >>> pypirc_both = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_both.txt') >>> print(open(pypirc_both).read()) [server-login] username:bdfl password:secret [distutils] index-servers = pypi local [pypi] username:bdfl password:secret [local] repository:http://localhost:8080/test/products username:bdfl password:secret >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_both) >>> pypiconfig.is_old_pypi_config() True >>> pypiconfig.is_new_pypi_config() True Parse the new format again and query a list of distutils servers: >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_new) >>> from pprint import pprint >>> pprint(sorted(pypiconfig.distutils_servers())) ['mycompany', 'plone.org', 'pypi'] If we have an old config file, the distutils server list is empty: >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_old) >>> pypiconfig.distutils_servers() [] And with a file with both an "old" and a "new" set of sections, the pypi entry is filtered out of the distutils servers list as that's handled by the "old" part: >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_both) >>> pprint(sorted(pypiconfig.distutils_servers())) ['local'] Asking for making a release or not ---------------------------------- Some people hardly ever want to make a full release of a package to pypi; a subversion tag may be enough. They can tell zest.releaser to use a different default answer when it asks to make a checkout for a release. This means you can usually just press Enter on all questions that zest.releaser asks. We try to read a [zest.releaser] section, either in pypirc or setup.cfg and look for a ``release`` option. We can ask for the result like this, which by default is True: >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_both) >>> pypiconfig.want_release() True We have a pypirc for this: >>> pypirc_no_release = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_no_release.txt') >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_no_release) >>> pypiconfig.want_release() False We can also specify to always do that checkout during a release: >>> pypirc_yes_release = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_yes_release.txt') >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_yes_release) >>> pypiconfig.want_release() True Creating a wheel ---------------- When the ``wheel`` package is installed, we could create shiny new Python wheels, next to the standard old-style source distributions. This seems good, but not for every package. So you have to explicitly turn it on. If the package is installed, we set a constant: >>> from zest.releaser.pypi import USE_WHEEL >>> USE_WHEEL True We try to read a [zest.releaser] section, either in pypirc or setup.cfg and check for a ``create-wheel`` option. In this case we explicitly disable checking for a setup.cfg, because when running the tests with ``tox`` the current directory is the base directory of zest.releaser, which now contains a setup.cfg. We can ask for the result like this, which by default is False: >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_both, use_setup_cfg=False) >>> pypiconfig.create_wheel() False We have a pypirc for this where we implicitly set it to False: >>> pypirc_no_release = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_no_release.txt') >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_no_release, use_setup_cfg=False) >>> pypiconfig.create_wheel() False We can also specify to always create it: >>> pypirc_yes_release = pkg_resources.resource_filename( ... 'zest.releaser.tests', 'pypirc_yes_release.txt') >>> pypiconfig = pypi.PypiConfig(config_filename=pypirc_yes_release, use_setup_cfg=False) >>> pypiconfig.create_wheel() True Fixing setup.cfg ---------------- A setup.cfg file can be used to influence the release process. This may contain options that are not advisable in the released package but should only be used during development. We can clean that up. First we prepare a directory. >>> pypi.SETUP_CONFIG_FILE 'setup.cfg' >>> import os >>> import shutil >>> import tempfile >>> tempdir = tempfile.mkdtemp() >>> os.chdir(tempdir) Without a setup.cfg there is no config: >>> setup_config = pypi.SetupConfig() >>> setup_config.config is None True >>> setup_config.has_bad_commands() False >>> setup_config.fix_config() >>> os.path.exists(pypi.SETUP_CONFIG_FILE) False Now we add a setup.cfg with some good and some bad commands: >>> SETUP_CFG = """ ... [zest.releaser] ... release = no ... ... [egg_info] ... tag_build = dev ... tag_svn_revision = true""" >>> dont_care = open(pypi.SETUP_CONFIG_FILE, 'w').write(SETUP_CFG) >>> setup_config = pypi.SetupConfig() >>> setup_config.has_bad_commands() True Fixing the config also prints the new config: >>> setup_config.fix_config() [zest.releaser] release = no [egg_info] tag_build = tag_svn_revision = false >>> os.path.exists(pypi.SETUP_CONFIG_FILE) True >>> print(''.join(open(pypi.SETUP_CONFIG_FILE).readlines())) [zest.releaser] release = no [egg_info] tag_build = tag_svn_revision = false We try that again with this fixed up config file as input: >>> setup_config = pypi.SetupConfig() >>> setup_config.has_bad_commands() False >>> setup_config.fix_config() >>> os.path.exists(pypi.SETUP_CONFIG_FILE) True >>> print(''.join(open(pypi.SETUP_CONFIG_FILE).readlines())) [zest.releaser] release = no [egg_info] tag_build = tag_svn_revision = false No-input mode ------------- In some cases you want no questions asked. Zest.releaser should just do its job without asking for versions or confirmations. You can enable this behaviour with a ``--no-input`` commandline option, but also by adding ``no-input = yes`` to the ``[zest.releaser]`` section in ``setup.cfg``. The default is False: >>> setup_config.no_input() False Enable the option: >>> SETUP_CFG = """ ... [zest.releaser] ... no-input = yes ... """ >>> dont_care = open(pypi.SETUP_CONFIG_FILE, 'w').write(SETUP_CFG) >>> setup_config = pypi.SetupConfig() >>> setup_config.no_input() True Python version file pointer --------------------------- In some cases you want to point at a Python file with a ``__version__`` marker in it. For that, there's the ``python-file-with-version`` option. The default is None: >>> setup_config.python_file_with_version() Enable the option: >>> SETUP_CFG = """ ... [zest.releaser] ... python-file-with-version = reinout/maurits.py ... """ >>> dont_care = open(pypi.SETUP_CONFIG_FILE, 'w').write(SETUP_CFG) >>> setup_config = pypi.SetupConfig() >>> setup_config.python_file_with_version() 'reinout/maurits.py'