
************
release-info
************

.. image:: https://sourceforge.net/p/release-info/code/ci/default/tree/_doc/_static/license.svg?format=raw
   :target: https://opensource.org/licenses/MIT

.. image:: https://sourceforge.net/p/release-info/code/ci/default/tree/_doc/_static/pypi.svg?format=raw
   :target: https://pypi.org/project/release-info/

.. image:: https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw
   :target: https://pypi.org/project/oitnb/

.. image:: https://sourceforge.net/p/ryd/code/ci/default/tree/_doc/_static/ryd.svg?format=raw
   :target: https://pypi.org/project/ryd/

For various reasons you might want to know which Python versions are "current":

- provide a list of pyXY targets to tox
- remove a no longer supported version of Python from the .whl files you generate
  to upload to PyPI
- which 'Programming Language :: Python ::' classifiers to include in your
  package information
- check if you have the latest micro version of a Python installed on your system

extracting this kind of information from various PEPs with slightly different
formats, python.org and other web pages, is cumbersome. This package both provides a commandline
utility ``python_release_info`` that you can use in scripts, makefiles, etc. and a programmtic
interface to get to release information.

The release information is retrieved from the internet and can be updated e.g.
only a daily basis after which build scripts can check if a new version needs to
be downloaded, compiled, installed. Pre-release information can be queried as well.

The following is a commandline session from March 28th, 2020 ( which you can simulate by specifying ``--dd 2020-03-28``)::

  $ /opt/python/3.8.2/bin/python -m venv --copies /opt/util/pri
  $ source /opt/util/pri/bin/activate
  (pri) $ pip install -U -q pip
  (pri) $ pip install -q release_info
  (pri) $ python_release_info current
  2.7.17
  3.5.9
  3.6.10
  3.7.7
  3.8.2
  (pri) $ python_release_info pre
  3.9.0.a.5
  (pri) $ echo $?    # 0 -> updated, 1 -> not updated
  1
  (pri) $ mkdir /opt/util/pri/tmp
  pri) $ ls --classify /opt/util/pri/tmp
  Python-3.8.2/  Python-3.8.2.tar.xz


If md5 information is available (true for all current and future versions), the downloaded tar file is checked.


CONFIG FILE
===========

Your config file normally is ``~/.config/python_release_info/config.ini``
(i.e. follows XDG) except for Windows: ``%APPDATA%/python_release_info/config.ini``.

The ``[INFO]`` section in that file allows you to add or overwrite data in the official information tree::

  [INFO]
  add = xxx.pon
  overwrite = yyy.pon

You can, but don't have to have either or both set, by default both are
commented out. The files must have the same hierarchical structure as the
``release_info.pon``, so it probably best to copy that file and delete what is
not relevant, then update the rest.

The difference between the two is that when using ``add`` the data loaded from
there does not override already existing "paths" to leaf node data loaded from
``release_info.pon``. You should use this for information that you expect to be
incorporated in future automatic updates, and when it does you want to use that
information. You should ``overwrite`` e.g. when you found a data error and cannot wait to
have it fixed in the ``release_info`` repository and automatically downloaded.


So if you would have the following in the automatically updated ``release_info.pon`` (in reality there is more data there)::

  dict(
    cpython={
      (3, 8): {
        (3, 8, 0): dict(rel=date(2019, 10, 14)),
        (3, 8, 1): dict(rel=date(2019, 12, 18)),
        (3, 8, 2): dict(rel=date(2020, 2, 24), md5='e9d6ebc92183a177b8e8a58cad5b8d67'),
      },
    }
  )

and have the following in your ``xxx.pon`` file (to be added)::

  dict(
    cpython={
      (3, 8): {
        (3, 8, 0): dict(md5='dbac8df9d8b9edc678d0f4cacdb7dbb0'),
        (3, 8, 1): dict(rel=date(2019, 12, 25), md5='b3fb85fd479c0bf950c626ef80cacb57'),
        (3, 8, 3): dict(rel=date(2020, 5, 4)),
      },
    }
  )

then the result would be like::

  dict(
    cpython={
      (3, 8): {
        (3, 8, 0): dict(rel=date(2019, 10, 14), md5='dbac8df9d8b9edc678d0f4cacdb7dbb0'),
        (3, 8, 1): dict(rel=date(2019, 12, 18), md5='b3fb85fd479c0bf950c626ef80cacb57'),
        (3, 8, 2): dict(rel=date(2020, 2, 24), md5='e9d6ebc92183a177b8e8a58cad5b8d67'),
        (3, 8, 3): dict(rel=date(2020, 5, 4)),
      },
    }
  )

but if the same content would be in ``yyy.pon`` (to be overwritten), then the result would
be like::

  dict(
    cpython={
      (3, 8): {
        (3, 8, 0): dict(rel=date(2019, 10, 14), md5='dbac8df9d8b9edc678d0f4cacdb7dbb0'),
        (3, 8, 1): dict(rel=date(2019, 12, 25), md5='b3fb85fd479c0bf950c626ef80cacb57'),
        (3, 8, 2): dict(rel=date(2020, 2, 24), md5='e9d6ebc92183a177b8e8a58cad5b8d67'),
        (3, 8, 3): dict(rel=date(2020, 5, 4)),
      },
    }
  )

with the difference being in the date for release 3.8.1.


API
===

You can use the release information from your program::

  import pathlib
  from release_info import release_info

  ri = release_info()
  ri.download_data()
  latest = None
  for version in ri.current():
      url = ri.src_url(version)
      print(version, url)
      latest = version
  path = pathlib.Path('/var/tmp')
  ri.download(latest, dir=path, extract=True)
  print(list(path.glob('Python*')))


which shows::

  (3, 5, 10) https://www.python.org/ftp/python/3.5.10/Python-3.5.10.tar.xz
  (3, 6, 11) https://www.python.org/ftp/python/3.6.11/Python-3.6.11.tar.xz
  (3, 7, 8) https://www.python.org/ftp/python/3.7.8/Python-3.7.8.tar.xz
  (3, 8, 5) https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tar.xz
  [PosixPath('/var/tmp/Python-3.8.5.tar.xz'), PosixPath('/var/tmp/Python-3.8.5')]
