{ "info": { "author": "Manuel Barkhau", "author_email": "mbarkhau@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Console", "Environment :: Other Environment", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# [PyCalVer: Automatic Calendar Versioning][repo_ref]\n\nPyCalVer is a cli tool to search and replace version strings in the files of\nyour project.\n\nBy default PyCalVer uses a format that looks like this:\n`v201812.0123-beta`, but it can be configured to generate version strings\nin many formats, including SemVer and other CalVer variants.\n\n\nProject/Repo:\n\n[![MIT License][license_img]][license_ref]\n[![Supported Python Versions][pyversions_img]][pyversions_ref]\n[![PyCalVer v201903.0030][version_img]][version_ref]\n[![PyPI Releases][pypi_img]][pypi_ref]\n[![PyPI Downloads][downloads_img]][downloads_ref]\n\nCode Quality/CI:\n\n[![Build Status][build_img]][build_ref]\n[![Type Checked with mypy][mypy_img]][mypy_ref]\n[![Code Coverage][codecov_img]][codecov_ref]\n[![Code Style: sjfmt][style_img]][style_ref]\n\n\n| Name | role | since | until |\n|-------------------------------------|-------------------|---------|-------|\n| Manuel Barkhau (mbarkhau@gmail.com) | author/maintainer | 2018-09 | - |\n\n\n\n\n\n[](TOC)\n\n- [Usage](#usage)\n - [Configuration](#configuration)\n - [Pattern Search and Replacement](#pattern-search-and-replacement)\n - [Examples](#examples)\n - [Version State](#version-state)\n - [The Current Version](#the-current-version)\n - [Bump It Up](#bump-it-up)\n- [The PyCalVer Format](#the-pycalver-format)\n - [Parsing](#parsing)\n - [Incrementing Behaviour](#incrementing-behaviour)\n - [Lexical Ids](#lexical-ids)\n- [Semantics of PyCalVer](#semantics-of-pycalver)\n - [Intentional Breaking Changes](#intentional-breaking-changes)\n - [Costs and Benefits](#costs-and-benefits)\n - [Unintentional Breaking Changes](#unintentional-breaking-changes)\n - [Pinning is not a Panacea](#pinning-is-not-a-panacea)\n - [Zeno's 1.0 and The Eternal Beta](#zeno-s-1-0-and-the-eternal-beta)\n\n[](TOC)\n\n\n## Usage\n\n### Configuration\n\nThe fastest way to setup a project is to use `pycalver init`.\n\n\n```shell\n$ pip install pycalver\n...\nInstalling collected packages: click pathlib2 typing toml six pycalver\nSuccessfully installed pycalver-201903.30\n\n$ pycalver --version\npycalver, version v201903.0030\n\n$ cd myproject\n~/myproject$ pycalver init --dry\nWARNING - File not found: pycalver.toml\nExiting because of '--dry'. Would have written to pycalver.toml:\n\n [pycalver]\n current_version = \"v201902.0001-alpha\"\n version_pattern = \"{pycalver}\"\n commit = true\n tag = true\n push = true\n\n [pycalver.file_patterns]\n \"README.md\" = [\n \"{version}\",\n \"{pep440_version}\",\n ]\n \"pycalver.toml\" = [\n 'current_version = \"{version}\"',\n ]\n```\n\nIf you already have a `setup.cfg` file, the `init` sub-command will write to that\ninstead.\n\n```\n~/myproject$ ls\nREADME.md setup.cfg setup.py\n\n~/myproject$ pycalver init\nWARNING - Couldn't parse setup.cfg: Missing [pycalver] section.\nUpdated setup.cfg\n```\n\nThis will add the something like the following to your `setup.cfg`\n(depending on what files already exist in your project):\n\n```ini\n# setup.cfg\n[pycalver]\ncurrent_version = \"v201902.0001-alpha\"\nversion_pattern = \"{pycalver}\"\ncommit = True\ntag = True\npush = True\n\n[pycalver:file_patterns]\nsetup.cfg =\n current_version = {version}\nsetup.py =\n \"{version}\",\n \"{pep440_version}\",\nREADME.md =\n {version}\n {pep440_version}\n```\n\nThis probably won't cover every version number used in your project and you\nwill have to manually add entries to `pycalver:file_patterns`. Something\nlike the following may illustrate additional changes you might need to\nmake.\n\n```ini\n[pycalver:file_patterns]\nsetup.cfg =\n current_version = {pycalver}\nsetup.py =\n version=\"{pep440_pycalver}\"\nsrc/mymodule_v*/__init__.py =\n __version__ = \"{pycalver}\"\nREADME.md =\n [PyCalVer {calver}{build}{release}]\n img.shields.io/static/v1.svg?label=PyCalVer&message={pycalver}&color=blue\n```\n\nTo see if a pattern is found, you can use `pycalver bump --dry`, which will\nleave your project files untouched and only show you a diff of the changes\nit would have made.\n\n```shell\n$ pycalver bump --dry --no-fetch\nINFO - Old Version: v201901.0001-beta\nINFO - New Version: v201902.0002-beta\n--- README.md\n+++ README.md\n@@ -11,7 +11,7 @@\n\n [![Supported Python Versions][pyversions_img]][pyversions_ref]\n-[![Version v201901.0001][version_img]][version_ref]\n+[![Version v201902.0002][version_img]][version_ref]\n [![PyPI Releases][pypi_img]][pypi_ref]\n\n--- src/mymodule_v1/__init__.py\n+++ src/mymodule_v1/__init__.py\n@@ -1,1 +1,1 @@\n-__version__ = \"v201901.0001-beta\"\n+__version__ = \"v201902.0002-beta\"\n\n--- src/mymodule_v2/__init__.py\n+++ src/mymodule_v2/__init__.py\n@@ -1,1 +1,1 @@\n-__version__ = \"v201901.0001-beta\"\n+__version__ = \"v201902.0002-beta\"\n\n--- setup.py\n+++ setup.py\n@@ -44,7 +44,7 @@\n name=\"myproject\",\n- version=\"201901.1b0\",\n+ version=\"201902.2b0\",\n license=\"MIT\",\n```\n\nIf there is no match for a pattern, bump will report an error.\n\n```shell\n$ pycalver bump --dry --no-fetch\nINFO - Old Version: v201901.0001-beta\nINFO - New Version: v201902.0002-beta\nERROR - No match for pattern 'img.shields.io/static/v1.svg?label=PyCalVer&message={pycalver}&color=blue'\nERROR - Pattern compiles to regex 'img\\.shields\\.io/static/v1\\.svg\\?label=PyCalVer&message=(?Pv(?P\\d{4})(?P(?:0[0-9]|1[0-2]))\\.(?P\\d{4,})(?:-(?P\n(?:alpha|beta|dev|rc|post|final)))?)&color=blue'\n```\n\nThe internally used regular expression is also shown, which you can use to debug the issue, for example on [regex101.com](https://regex101.com/r/ajQDTz/2).\n\n\n### Pattern Search and Replacement\n\nThe `pycalver:file_patterns` section of the configuration is used both to search\nand also to replace version strings in your projects files. Everything except\nfor valid placeholders is treated as literal text. Available placeholders are:\n\n\n| placeholder | range / example(s) | comment |\n|---------------------|---------------------|-----------------|\n| `{pycalver}` | v201902.0001-beta | |\n| `{pep440_pycalver}` | 201902.1b0 | |\n| `{year}` | 2019... | `%Y` |\n| `{yy}` | 18, 19..99, 01, 02 | `%y` |\n| `{quarter}` | 1, 2, 3, 4 | |\n| `{month}` | 09, 10, 11, 12 | `%m` |\n| `{iso_week}` | 00..53 | `%W` |\n| `{us_week}` | 00..53 | `%U` |\n| `{dom}` | 01..31 | `%d` |\n| `{doy}` | 001..366 | `%j` |\n| `{build}` | .0123 | lexical id |\n| `{build_no}` | 0123, 12345 | ... |\n| `{release}` | -alpha, -beta, -rc | --release= |\n| `{release_tag}` | alpha, beta, rc | ... |\n| `{semver}` | 1.2.3 | |\n| `{MAJOR}` | 1..9, 10..99, 100.. | --major |\n| `{MINOR}` | 1..9, 10..99, 100.. | --minor |\n| `{PATCH}` | 1..9, 10..99, 100.. | --patch |\n\n\nThere are some limitations to keep in mind:\n\n 1. A version string cannot span multiple lines.\n 2. Characters generated by a placeholder cannot be escaped.\n 3. The timezone is always UTC.\n\nThe lack of escaping may for example be an issue with badge URLs.\nYou may want to put the following text in your README.md (note\nthat shields.io parses the two \"-\" dashes before `beta` as one\nliteral \"-\"):\n\n```\nhttps://img.shields.io/badge/myproject-v201812.0116--beta-blue.svg\n```\n\nWhile you could use the following pattern, which will work fine for a\nwhile:\n\n```ini\nREADME.md =\n /badge/myproject-v{year}{month}.{build_no}--{release_tag}-blue.svg\n```\n\nEventually this will break, when you do a `final` release, at\nwhich point the following will be put in your README.md:\n\n```\nhttps://img.shields.io/badge/myproject-v201812.0117--final-blue.svg\n```\n\nWhen what you probably wanted was this (with the `--final` tag omitted):\n\n```\nhttps://img.shields.io/badge/myproject-v201812.0117-blue.svg\n```\n\n### Examples\n\nThe easiest way to test a pattern is with the `pycalver test` sub-command.\n\n```shell\n$ pycalver test 'v18w01' 'v{yy}w{iso_week}'\nNew Version: v19w06\nPEP440 : v19w06\n\n$ pycalver test 'v18.01' 'v{yy}w{iso_week}'\nERROR - Invalid version string 'v18.01' for pattern\n 'v{yy}w{iso_week}'/'v(?P\\d{2})w(?P(?:[0-4]\\d|5[0-3]))'\nERROR - Invalid version 'v18.01' and/or pattern 'v{yy}w{iso_week}'.\n```\n\nAs you can see, each pattern is internally translated to a regular\nexpression.\n\nThe `pycalver test` sub-command accepts the same cli flags as `pycalver\nbump` to update the components that are not updated automatically (eg.\nbased on the calendar).\n\n```shell\n$ pycalver test 'v18.1.1' 'v{yy}.{MINOR}.{PATCH}'\nNew Version: v19.1.1\nPEP440 : 19.1.1\n\n$ pycalver test 'v18.1.1' 'v{yy}.{MINOR}.{PATCH}' --patch\nNew Version: v19.1.2\nPEP440 : 19.1.2\n\n$ pycalver test 'v18.1.2' 'v{yy}.{MINOR}.{PATCH}' --minor\nNew Version: v19.2.0\nPEP440 : 19.2.0\n\n$ pycalver test 'v201811.0051-beta' '{pycalver}'\nNew Version: v201902.0052-beta\nPEP440 : 201902.52b0\n\n$ pycalver test 'v201811.0051-beta' '{pycalver}' --release rc\nNew Version: v201902.0052-rc\nPEP440 : 201902.52rc0\n\n$ pycalver test 'v201811.0051-beta' '{pycalver}' --release final\nNew Version: v201902.0052\nPEP440 : 201902.52\n```\n\nNote that pypi/setuptools/pip will normalize version strings to a format\ndefined in [PEP440][pep_440_ref]. You can use a format that deviates from\nthis, just be aware that version strings processed by these tools will look\ndifferent.\n\n\n### Version State\n\nThe \"current version\" is considered global state that needs to be\nstored somewhere. Typically this might be stored in a `VERSION`\nfile, or some other file which is part of the repository. This\ncreates the risk that parallel branches can have different\nstates. If the \"current version\" were defined only by files in\nthe local checkout, the same version might be generated for\ndifferent commits.\n\nTo avoid this issue, pycalver treats VCS tags as the canonical /\n[SSOT][ssot_ref] for the most recent version and attempts to\nchange this state in the most atomic way possible. This is why\nsome actions of the `pycalver` command can take a while, as it is\nsynchronizing with the remote repository to get the most recent\nversions and to push any new version tags as soon as possible.\n\n\n### The Current Version\n\nThe current version that will be bumped is defined either as\n\n - Typically: The lexically largest git/mercurial tag which matches the\n `version_pattern` from your config.\n - Initially: Before any tags have been created (or you're not using a\n supported VCS), the value of `pycalver.current_version` in `setup.cfg` /\n `pyproject.toml` / `pycalver.toml`.\n\nAs part of doing `pycalver bump`, your local VCS index is updated using\n`git fetch --tags`/`hg pull`. This reduces the risk that some tags are\nunknown locally and makes it less likely that the same version string is\ngenerated for different commits, which would result in an ambiguous version\ntag. This can happen if multiple maintainers produce a release at the same\ntime or if a build system is triggered multiple times and multiple builds\nrun concurrently to each other. For a small project (with only one\nmaintainer and no build system) this is a non-issue and you can always use\n`-n/--no-fetch` to skip updating the tags.\n\n```shell\n$ time pycalver show --verbose\nINFO - fetching tags from remote (to turn off use: -n / --no-fetch)\nINFO - Working dir version : v201812.0018\nINFO - Latest version from git tag: v201901.0019-beta\nCurrent Version: v201901.0019-beta\nPEP440 : 201901.19b0\n\nreal 0m4,254s\n\n$ time pycalver show --verbose --no-fetch\n...\nreal 0m0,840s\n```\n\n\n### Bump It Up\n\nTo increment the current version and publish a new version, you can use the\n`pycalver bump` sub-command. `bump` is configured in the `pycalver` config\nsection:\n\n```ini\n[pycalver]\ncurrent_version = \"v201812.0006-beta\"\nversion_pattern = \"{pycalver}\"\ncommit = True\ntag = True\npush = True\n```\n\nThis configuration is appropriate to create a commit which\n\n1. contains the changes to the version strings,\n2. contains no other changes (unrelated to bumping the version),\n3. is tagged with the new version,\n4. has a version tag that is unique in the repository.\n\nIn order to make sure only changes to version strings are in the commit,\nyou need to make sure you have a clean VCS checkout when you invoke\n`pycalver bump`.\n\nThe steps performed by `bump` are:\n\n0. Check that your repo doesn't have any local changes.\n1. *Fetch* the most recent global VCS tags from origin\n (`-n`/`--no-fetch` to disable).\n2. Generate a new version, incremented from the current version.\n3. Update version strings in all files configured in `file_patterns`.\n4. *Commit* the updated version strings.\n5. *Tag* the new commit.\n6. *Push* the new commit and tag.\n\nAgain, you can use `--dry` to inspect the changes first.\n\n```\n$ pycalver bump --dry\n--- setup.cfg\n+++ setup.cfg\n@@ -65,7 +65,7 @@\n\n [pycalver]\n-current_version = v201812.0005-beta\n+current_version = v201812.0006-beta\n commit = True\n tag = True\n push = True\n...\n```\n\nIf everything looks OK, you can do `pycalver bump`.\n\n```\n$ pycalver bump --verbose\nINFO - fetching tags from remote (to turn off use: -n / --no-fetch)\nINFO - Old Version: v201812.0005-beta\nINFO - New Version: v201812.0006-beta\nINFO - git commit --file /tmp/tmpph_npey9\nINFO - git tag --annotate v201812.0006-beta --message v201812.0006-beta\nINFO - git push origin v201812.0006-beta\n```\n\n\n## The PyCalVer Format\n\nThe PyCalVer format for version strings has three parts:\n\n```\n\n o Year and Month of Release\n | o Sequential Build Number\n | | o Release Tag (optional)\n | | |\n ---+--- --+-- --+--\n v201812 .0123 -beta\n\n\n```\n\nSome examples:\n\n```\nv201711.0001-alpha\nv201712.0027-beta\nv201801.0031\nv201801.0032-post\n...\nv202207.18133\nv202207.18134\n```\n\nThis slightly verbose format was chosen in part to be distinctive from\nothers, so that users of your package can see at a glance that your project\nwill strive to maintain the one semantic that really matters: **newer ==\nbetter**.\n\nTo convince you of the merits of not breaking things, here are some\nresources which PyCalVer was inspired by:\n\n - [\"Speculation\" talk by Rich\n Hicky](https://www.youtube.com/watch?v=oyLBGkS5ICk)\n - [Designing a Version by Mahmoud\n Hashemi](http://sedimental.org/designing_a_version.html)\n - [calver.org](https://calver.org/)\n - [\"The cargo cult of versioning\" by Kartik\n Agaram](http://akkartik.name/post/versioning)\n - The [bumpversion][bumpversion_ref] project, upon which\n PyCalVer is partially based.\n - [\"Our Software Dependency Problem\" by Russ Cox](https://research.swtch.com/deps)\n\n\n### Parsing\n\nThese version strings can be parsed with the following regular expression:\n\n```python\nimport re\n\n# https://regex101.com/r/fnj60p/10\nPYCALVER_PATTERN = r\"\"\"\n\\b\n(?P\n (?P\n v # \"v\" version prefix\n (?P\\d{4})\n (?P\\d{2})\n )\n (?P\n \\. # \".\" build nr prefix\n (?P\\d{4,})\n )\n (?P\n \\- # \"-\" release prefix\n (?Palpha|beta|dev|rc|post)\n )?\n)(?:\\s|$)\n\"\"\"\nPYCALVER_REGEX = re.compile(PYCALVER_PATTERN, flags=re.VERBOSE)\n\nversion_str = \"v201712.0001-alpha\"\nversion_match = PYCALVER_REGEX.match(version_str)\n\nassert version_match.groupdict() == {\n \"pycalver\" : \"v201712.0001-alpha\",\n \"vYYYYMM\" : \"v201712\",\n \"year\" : \"2017\",\n \"month\" : \"12\",\n \"build\" : \".0001\",\n \"build_no\" : \"0001\",\n \"release\" : \"-alpha\",\n \"release_tag\": \"alpha\",\n}\n\nversion_str = \"v201712.0033\"\nversion_match = PYCALVER_REGEX.match(version_str)\n\nassert version_match.groupdict() == {\n \"pycalver\" : \"v201712.0033\",\n \"vYYYYMM\" : \"v201712\",\n \"year\" : \"2017\",\n \"month\" : \"12\",\n \"build\" : \".0033\",\n \"build_no\" : \"0033\",\n \"release\" : None,\n \"release_tag\": None,\n}\n```\n\n### Incrementing Behaviour\n\nTo see how version strings are incremented, we can use\n`pycalver test`:\n\n```shell\n$ pycalver test v201801.0033-beta\nNew Version: v201902.0034-beta\nPEP440 : 201902.34b0\n```\n\nThis is the simple case:\n\n - The calendar component is updated to the current year and\n month.\n - The build number is incremented by 1.\n - The optional release tag is preserved as is.\n\nYou can explicitly update the release tag by using the\n`--release=` argument:\n\n```shell\n$ pycalver test v201801.0033-alpha --release=beta\nNew Version: v201902.0034-beta\nPEP440 : 201902.34b0\n\n$ pycalver test v201902.0034-beta --release=final\nNew Version: v201902.0035\nPEP440 : 201902.35\n```\n\nTo maintain lexical ordering of version numbers, the version number is padded\nwith extra zeros (see [Lexical Ids](#lexical-ids) ).\n\n\n### Lexical Ids\n\nThe build number padding may eventually be exhausted. In order to preserve\nlexical ordering, build numbers for the `{build_no}` pattern are\nincremented in a special way. Examples will perhaps illustrate more\nclearly.\n\n```python\n\"0001\"\n\"0002\"\n\"0003\"\n...\n\"0999\"\n\"11000\"\n\"11001\"\n...\n\"19998\"\n\"19999\"\n\"220000\"\n\"220001\"\n```\n\nWhat is happening here is that the left-most digit is incremented\nearly/preemptively. Whenever the left-most digit would change, the padding\nof the id is expanded through a multiplication by 11.\n\n```python\n>>> prev_id = \"0999\"\n>>> num_digits = len(prev_id)\n>>> num_digits\n4\n>>> prev_int = int(prev_id, 10)\n>>> prev_int\n999\n>>> maybe_next_int = prev_int + 1\n>>> maybe_next_int\n1000\n>>> maybe_next_id = f\"{maybe_next_int:0{num_digits}}\"\n>>> maybe_next_id\n\"1000\"\n>>> is_padding_ok = prev_id[0] == maybe_next_id[0]\n>>> is_padding_ok\nFalse\n>>> if is_padding_ok:\n... # normal case\n... next_id = maybe_next_id\n... else:\n... # extra padding needed\n... next_int = maybe_next_int * 11\n... next_id = str(next_int)\n>>> next_id\n\"11000\"\n```\n\nThis behaviour ensures that the following semantic is always preserved:\n`new_version > old_version`. This will be true, regardless of padding\nexpansion. To illustrate the issue this solves, consider what would happen\nif we did not expand the padding and instead just incremented numerically.\n\n```python\n\"0001\"\n\"0002\"\n\"0003\"\n...\n\"0999\"\n\"1000\"\n...\n\"9999\"\n\"10000\"\n```\n\nHere we eventually run into a build number where the lexical ordering is\nnot preserved, since `\"10000\" > \"9999\" == False` (because the string `\"1\"`\nis lexically smaller than `\"9\"`). With large enough padding this may be a\nnon issue, but it's better to not have to think about it.\n\nJust as an example of why lexical ordering is a nice property to have,\nthere are lots of software which read git tags, but which have no logic to\nparse version strings. This software can nonetheless order the version tags\ncorrectly using commonly available lexical ordering. At the most basic\nlevel it can allow you to use the UNIX `sort` command, for example to parse\nVCS tags.\n\n\n```shell\n$ printf \"v0.9.0\\nv0.10.0\\nv0.11.0\\n\" | sort\nv0.10.0\nv0.11.0\nv0.9.0\n\n$ printf \"v0.9.0\\nv0.10.0\\nv0.11.0\\n\" | sort -n\nv0.10.0\nv0.11.0\nv0.9.0\n\n$ printf \"0998\\n0999\\n11000\\n11001\\n11002\\n\" | sort\n0998\n0999\n11000\n11001\n11002\n```\n\nThis sorting even works correctly in JavaScript!\n\n```\n> var versions = [\"11002\", \"11001\", \"11000\", \"0999\", \"0998\"];\n> versions.sort();\n[\"0998\", \"0999\", \"11000\", \"11001\", \"11002\"]\n```\n\n\n## Semantics of PyCalVer\n\n> Disclaimer: This section can of course only be aspirational. There is nothing\n> to prevent package maintainers from publishing packages with different\n> semantics than what is presented here.\n\nPyCalVer places a greater burden on package maintainers than SemVer.\nBackward incompatibility is not encoded in the version string, because\n**maintainers should not intentionally introduce breaking changes**. This\nis great for users of a package, who can worry a bit less about an update\ncausing their project to break. A paranoid user can of course still pin to\na known good version, and freezing dependencies for deployments is still a\ngood practice, but for development, users ideally shouldn't need any\nversion specifiers in their requirements.txt. This way they always get the\nnewest bug fixes and features.\n\nPart of the reason for the distinctive PyCalVer version string, is for\nusers to be able to recognize, just from looking at the version string,\nthat a package comes with the promise (or at least aspiration) that it\nwon't break, that it is safe for users to update. Compare this to a SemVer\nversion string, where maintainers explicitly state that an update _might_\nbreak their program and that they _may_ have to do extra work after\nupdating and even if it hasn't in the past, the package maintainers\nanticipate that they might make such breaking changes in the future.\n\nIn other words, the onus is on the user of a package to update their\nsoftware, if they want to update to the latest version of a package. With\nPyCalVer the onus is on package maintainer to maintain backward\ncompatibility.\n\nIdeally users can trust the promise of a maintainer that the following\nsemantics will always be true:\n\n - Newer is compatible.\n - Newer has fewer bugs.\n - Newer has more features.\n - Newer has equal or better performance.\n\nAlas, the world is not ideal. So how do users and maintainers deal with changes\nthat violate these promises?\n\n\n### Intentional Breaking Changes\n\n> Namespaces are a honking great idea\n> - let's do more of those!\n>\n> - The Zen of Python\n\nIf you must make a breaking change to a package, **instead of incrementing a\nnumber**, the recommended approach with PyCalVer is to **create a whole new\nnamespace**. Put differently, the major version becomes part of the name of the\nmodule or even of the package. Typically you might add a numerical suffix, eg.\n`mypkg -> mypkg2`.\n\nIn the case of python distributions, you can include multiple module\npackages like this.\n\n```python\n# setup.py\nsetuptools.setup(\n name=\"my-package\",\n license=\"MIT\",\n packages=[\"mypkg\", \"mypkg2\"],\n package_dir={\"\": \"src\"},\n ...\n)\n```\n\nIn other words, you can ship older versions side by side with newer ones,\nand users can import whichever one they need. Alternatively you can publish\na new package distribution, with new namespace, but please consider also\nrenaming the module.\n\n```python\n# setup.py\nsetuptools.setup(\n name=\"my-package-v2\",\n license=\"MIT\",\n packages=[\"mypkg2\"],\n package_dir={\"\": \"src\"},\n ...\n)\n```\n\nUsers will have an easier time working with your package if `import mypkg2`\nis enough to determine which version of your project are using. A further\nbenefit of creating multiple modules is that users can import both old and\nnew modules in the same environment and can use some packages which depend\non the old version as well as some that depend on the new version. The\ndownside for users, is that they may have to do minimal changes to their\ncode, even if the breaking change did not affect them.\n\n```diff\n- import mypkg\n+ import mypkg2\n\n def usage_code():\n- mypkg.myfun()\n+ mypkg2.myfun()\n```\n\n\n### Costs and Benefits\n\nIf this seems like overkill because it's a lot of work for you as a\nmaintainer, consider first investing some time in your tools, so you\nminimize future work required to create new packages. I've [done this for\nmy personal projects][bootstrapit_ref], but you may find [other\napproaches][cookiecutter_ref] to be more appropriate for your use.\n\nIf this seems like overkill because you're not convinced that imposing a\nvery small burden on users is such a big deal, consider that your own\nprojects may indirectly depend on dozens of libraries which you've never\neven heard of. If every maintainer introduced breaking changes only once\nper year, users who depend on only a dozen libraries would be dealing with\npackaging issues every month! In other words: *Breaking things is a big\ndeal*. A bit of extra effort for a few maintainers seems like a fair trade\nto lower the effort imposed on many users, who would be perfectly happy to\ncontinue using the old code until _they_ decide when to upgrade.\n\n\n### Unintentional Breaking Changes\n\nThe other kind of breaking change is the non-intentional kind, otherwise\nknown as a \"bug\" or \"regression\". Realize first of all, that it is\nimpossible for any versioning system to encode that this has happened:\nSince the maintainer isn't knowingly introducing a bug they naturally can't\nupdate their version numbers to reflect what they don't know about. Instead\nwe have to deal with these issues after the fact.\n\nThe first thing a package maintainer can do is to minimize the chance of\ninflicting buggy software on users. After any non-trivial (potentially breaking)\nchange, it is a good practice to first create an `-alpha`/`-beta`/`-rc` release.\nThese so called `--pre` releases are intended to be downloaded only by the few\nand the brave: Those who are willing to participate in testing. After any issues\nare ironed out with the `--pre` releases, a `final` release can be made for the\nwider public.\n\nNote that the default behaviour of `pip install ` (without any version\nspecifier) is to download the latest `final` release. It will download a `--pre`\nrelease *only* if\n\n 1. no `final` release is available\n 2. the `--pre` flag is explicitly used, or\n 3. if the requirement specifier _explicitly_ includes the version number of a\n pre release, eg. `pip install mypkg==v201812.0007-alpha`.\n\nShould a release include a bug (heaven forbid and despite all precautions),\nthen the maintainer should publish a new release which either fixes the bug\nor reverts the change. If users previously downloaded a version of the\npackage which included the bug, they only have to do `pip install --upgrade\n` and the issue will be resolved.\n\nPerhaps a timeline will illustrate more clearly:\n\n```\nv201812.0665 # last stable release\nv201812.0666-beta # pre release for testers\nv201901.0667 # final release after testing\n\n# bug is discovered which effects v201812.0666-beta and v201901.0667\n\nv201901.0668-beta # fix is issued for testers\nv201901.0669 # fix is issued everybody\n\n# Alternatively, revert before fixing\n\nv201901.0668 # same as v201812.0665\nv201901.0669-beta # reintroduce change from v201812.0666-beta + fix\nv201901.0670 # final release after testing\n```\n\nIn the absolute worst case, a change is discovered to break backward\ncompatibility, but the change is nonetheless considered to be desirable. At that\npoint, a new release should be made to revert the change.\n\nThis allows 1. users who _were_ exposed to the breaking change to update to the\nlatest release and get the old (working) code again, and 2. users who _were not_\nexposed to the breaking change to never even know anything was broken.\n\nRemember that the goal is to always make things easy for users who have\nyour package as a dependency. If there is any issue whatsoever, all they\nshould have to do is `pip install --update`. If this doesn't work, they may\nhave to *temporarily* pin to a known good version, until a fixed release\nhas been published.\n\nAfter this immediate fire has been extinguished, if the breaking change is\nworth keeping, then **create a new module or even a new package**. This\npackage will perhaps have 99% overlap to the previous one and the old one\nmay eventually be abandoned.\n\n```\nmypkg v201812.0665 # last stable release\nmypkg v201812.0666-rc # pre release for testers\nmypkg v201901.0667 # final release after testing period\n\n# bug is discovered in v201812.0666-beta and v201901.0667\n\nmypkg v201901.0668 # same as v201812.0665\n\n# new package is created with compatibility breaking code\n\nmypkg2 v201901.0669 # same as v201901.0667\nmypkg v201901.0669 # updated readme, declaring support\n # level for mypkg, pointing to mypgk2\n # and documenting how to upgrade.\n```\n\n\n### Pinning is not a Panacea\n\nFreezing your dependencies by using `pip freeze` to create a file with packages\npinned to specific version numbers is great to get a stable and repeatable\ndeployment.\n\nThe main problem with pinning is that it is another burden imposed on users,\nand it is a burden which in practice only some can bear. The vast majority of\nusers either 1) pin their dependencies and update them without determining what\nchanged or if it is safe for them to update, or 2) pin their dependencies and\nforget about them. In case 1 the only benefit is that users might at least be\naware of when an update happened, so they can perhaps correlate that a new bug\nin their software might be related to a recent update. Other than that, keeping\ntabs on dependencies and updating without diligence is hardly better than not\nhaving pinned at all. In case 2, an insurmountable debt will pile up and the\ndependencies of a project are essentially frozen in the past.\n\nYes, it is true that users will be better off if they have sufficient test\ncoverage to determine for themselves that their code is not broken even after\ntheir dependencies are updated. It is also true however, that a package\nmaintainer is usually in a better position to judge if a change might cause\nsomething to break.\n\n\n### Zeno's 1.0 and The Eternal Beta\n\nThere are two opposite approaches to backward compatibility which find a\nreflection in the version numbers they use. In the case of SemVer, if a\nproject has a commitment to backward compatibility, it may end up never\nincriminating the major version, leading to the [Zeno 1.0\nparadox][zeno_1_dot_0_ref]. On the other end are projects that avoid any\ncommitment to backward compatibility and forever keep the \"beta\" label.\n\nOf course an unpaid Open Source developer *does not owe anybody a\ncommitment to backward compatibility*. Especially when a project is young\nand going through major changes, such a commitment may not make any sense.\nFor these cases you can still use PyCalVer, just so long as there is a big\nfat warning at the top of your README, that your project is not ready for\nproduction yet.\n\nNote that there is a difference between software that is considered to be\nin a \"beta\" state and individual releases which have a `-beta` tag. These\ndo not mean the same thing. In the case of releases of python packages, the\nrelease tag (`-alpha`, `-beta`, `-rc`) says something about the stability\nof a *particular release*. This is similar ([perhaps\nidentical][pep_101_ref]) to the meaning of release tags used by the CPython\ninterpreter. A release tag is not a statement about the general stability\nof the software as a whole, it is metadata about a particular release\nartifact of a package, eg. a `.whl` file.\n\n\n[repo_ref]: https://gitlab.com/mbarkhau/pycalver\n\n[setuptools_ref]: https://setuptools.readthedocs.io/en/latest/setuptools.html#specifying-your-project-s-version\n\n[ssot_ref]: https://en.wikipedia.org/wiki/Single_source_of_truth\n\n[pep_440_ref]: https://www.python.org/dev/peps/pep-0440/\n\n[zeno_1_dot_0_ref]: http://sedimental.org/designing_a_version.html#semver-and-release-blockage\n\n[pep_101_ref]: https://www.python.org/dev/peps/pep-0101/\n\n[bumpversion_ref]: https://github.com/peritus/bumpversion\n\n[bootstrapit_ref]: https://gitlab.com/mbarkhau/bootstrapit\n\n[cookiecutter_ref]: https://cookiecutter.readthedocs.io\n\n\n[build_img]: https://gitlab.com/mbarkhau/pycalver/badges/master/pipeline.svg\n[build_ref]: https://gitlab.com/mbarkhau/pycalver/pipelines\n\n[codecov_img]: https://gitlab.com/mbarkhau/pycalver/badges/master/coverage.svg\n[codecov_ref]: https://mbarkhau.gitlab.io/pycalver/cov\n\n[license_img]: https://img.shields.io/badge/License-MIT-blue.svg\n[license_ref]: https://gitlab.com/mbarkhau/pycalver/blob/master/LICENSE\n\n[mypy_img]: https://img.shields.io/badge/mypy-checked-green.svg\n[mypy_ref]: https://mbarkhau.gitlab.io/pycalver/mypycov\n\n[style_img]: https://img.shields.io/badge/code%20style-%20sjfmt-f71.svg\n[style_ref]: https://gitlab.com/mbarkhau/straitjacket/\n\n[downloads_img]: https://pepy.tech/badge/pycalver/month\n[downloads_ref]: https://pepy.tech/project/pycalver\n\n[version_img]: https://img.shields.io/static/v1.svg?label=PyCalVer&message=v201903.0030&color=blue\n[version_ref]: https://pypi.org/project/pycalver/\n\n[pypi_img]: https://img.shields.io/badge/PyPI-wheels-green.svg\n[pypi_ref]: https://pypi.org/project/pycalver/#files\n\n[pyversions_img]: https://img.shields.io/pypi/pyversions/pycalver.svg\n[pyversions_ref]: https://pypi.python.org/pypi/pycalver\n\n\n\n# Changelog for https://gitlab.com/mbarkhau/pycalver\n\n\n## v201903.0030\n\n - Fix: Use pattern from config instead of hardcoded {pycalver} pattern.\n - Fix: Better error messages for git/hg issues.\n - Add: Implicit default pattern for config file.\n\n## v201903.0028\n\n - Fix: Add warnings when configured files are not under version control.\n - Add: Coloured output for bump --dry\n\n\n## v201902.0027\n\n - Fix: Allow --release=post\n - Fix: Better error reporting for bad patterns\n - Fix: Regex escaping issue with \"?\"\n\n\n## v201902.0024\n\n - Added: Support for globs in file patterns.\n - Fixed: Better error reporting for invalid config.\n\n\n## v201902.0020\n\n - Added: Support for many more custom version patterns.\n\n\n## v201812.0018\n\n - Fixed: Better handling of pattern replacements with \"-final\" releases.\n\n\n## v201812.0017\n\n - Fixed #2 on github. `pycalver init` was broken.\n - Fixed pattern escaping issues.\n - Added lots more tests for cli.\n - Cleaned up documentation.\n\n\n## v201812.0011-beta\n\n - Add version tags using git/hg.\n - Use git/hg tags as SSOT for most recent version.\n - Start using https://gitlab.com/mbarkhau/bootstrapit\n - Move to https://gitlab.com/mbarkhau/pycalver\n\n\n## v201809.0001-alpha\n\n - Initial release\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://gitlab.com/mbarkhau/pycalver", "keywords": "version versioning bumpversion calver", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "pycalver", "package_url": "https://pypi.org/project/pycalver/", "platform": "", "project_url": "https://pypi.org/project/pycalver/", "project_urls": { "Homepage": "https://gitlab.com/mbarkhau/pycalver" }, "release_url": "https://pypi.org/project/pycalver/201903.30/", "requires_dist": [ "pathlib2", "typing", "click", "toml", "six" ], "requires_python": "", "summary": "CalVer for python libraries.", "version": "201903.30" }, "last_serial": 5582595, "releases": { "201809.2b0": [ { "comment_text": "", "digests": { "md5": "53d34dac7100b3304cf829d3384a65ac", "sha256": "4ac7e29b90e3939b05dc0d01543b0baba227dfd08bbdcc360913b8e9b9b24575" }, "downloads": -1, "filename": "pycalver-201809.2b0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "53d34dac7100b3304cf829d3384a65ac", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 17884, "upload_time": "2018-10-16T07:04:32", "url": "https://files.pythonhosted.org/packages/02/bd/61e9d5b9b0d0c72a24e5c3b3d9848e598481f3c4f8f1e0aadc648b13c8d4/pycalver-201809.2b0-py2.py3-none-any.whl" } ], "201812.11b0": [ { "comment_text": "", "digests": { "md5": "305fb92ec2d1912d5a41d4e954c56b35", "sha256": "6b0fd6fd274f946bbe89704f0a7be84267d93800b26faf017077c85bc39caa27" }, "downloads": -1, "filename": "pycalver-201812.11b0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "305fb92ec2d1912d5a41d4e954c56b35", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 24465, "upload_time": "2018-12-09T17:31:57", "url": "https://files.pythonhosted.org/packages/c7/62/72f23e11e2329c9b5bb9cb721c4c3bb77e7047e854225bbca4fb8a73c88a/pycalver-201812.11b0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "c9a7861000f757c108cb381c19b3887a", "sha256": "2f61330c4edcd6abde0d5b989107080099d535ed26118c7fe633286a30d3e048" }, "downloads": -1, "filename": "pycalver-201812.11b0.tar.gz", "has_sig": false, "md5_digest": "c9a7861000f757c108cb381c19b3887a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35214, "upload_time": "2018-12-09T17:31:58", "url": "https://files.pythonhosted.org/packages/8c/96/b12960d0d42a9203d3d1a653a4cc465e4e15b6d991c5a1ff64540fb9a35b/pycalver-201812.11b0.tar.gz" } ], "201812.17": [ { "comment_text": "", "digests": { "md5": "916dcee6aa129137e88d371fb846a51d", "sha256": "b374a7dc3e16d275f2a3c049781bd73a7d74d625e1699570b9460cfa664aa49e" }, "downloads": -1, "filename": "pycalver-201812.17-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "916dcee6aa129137e88d371fb846a51d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 25404, "upload_time": "2018-12-22T00:14:16", "url": "https://files.pythonhosted.org/packages/b7/35/8181f82d1f6333edf89d7a8608d1e5f2df568099a2e748892b4ae5e8b3d0/pycalver-201812.17-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7cf1dc61682db6944c1de2408e5e6a42", "sha256": "5b5c2924bbce9a52675bacb816e944f16b014d512cb1ea5c12bb8bdb1a5c76b4" }, "downloads": -1, "filename": "pycalver-201812.17.tar.gz", "has_sig": false, "md5_digest": "7cf1dc61682db6944c1de2408e5e6a42", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 38284, "upload_time": "2018-12-22T00:14:17", "url": "https://files.pythonhosted.org/packages/e1/61/261e21c147d415adf81160ee627629e3f44659400e51312cf9ceda327a51/pycalver-201812.17.tar.gz" } ], "201812.18": [ { "comment_text": "", "digests": { "md5": "8cfbef75f562628c459b1a09f932002f", "sha256": "414b7b5eab606d6956d0f6c1cf716d35fe571fa7e53324e16723c95e83db5132" }, "downloads": -1, "filename": "pycalver-201812.18-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8cfbef75f562628c459b1a09f932002f", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 25555, "upload_time": "2018-12-22T08:55:03", "url": "https://files.pythonhosted.org/packages/3e/c8/6097d8b645d2faebcfa977853be7bd2c5c73225b44fb7d850175a24a267b/pycalver-201812.18-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "7f930f6c7d84190ba88910d25dae96ad", "sha256": "96143347ea81b32985c3398bd21f354644453d0292e75cd58eed061172f8fdee" }, "downloads": -1, "filename": "pycalver-201812.18.tar.gz", "has_sig": false, "md5_digest": "7f930f6c7d84190ba88910d25dae96ad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 38850, "upload_time": "2018-12-22T08:55:05", "url": "https://files.pythonhosted.org/packages/de/41/4e74ea576ed3279b9bd2b20d51fce371551dce2c76afab63c9002113cd0f/pycalver-201812.18.tar.gz" } ], "201902.20": [ { "comment_text": "", "digests": { "md5": "e8775677b8963ea91eb5bf75d40b0580", "sha256": "b2a1ee4174acebe9c1f35c64f1acdfed0d48135741af1dc0829ade6672f611e3" }, "downloads": -1, "filename": "pycalver-201902.20-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e8775677b8963ea91eb5bf75d40b0580", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 32831, "upload_time": "2019-02-16T16:20:07", "url": "https://files.pythonhosted.org/packages/e2/2d/8a56b81108d663041a0ec19dc06e5f4ff56ddfe31783edfd67183698231e/pycalver-201902.20-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "418e78e85938a66e3a74986d48986c31", "sha256": "e5c6dd78d66b41d8b3729df66379d375c90759301335d9bc19e3dd4f28e6aa7f" }, "downloads": -1, "filename": "pycalver-201902.20.tar.gz", "has_sig": false, "md5_digest": "418e78e85938a66e3a74986d48986c31", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59002, "upload_time": "2019-02-16T16:20:08", "url": "https://files.pythonhosted.org/packages/2d/4d/31fef46d5f459a6328c62dc3c657622ec5b082786950e6216d11c86cac8d/pycalver-201902.20.tar.gz" } ], "201902.24": [ { "comment_text": "", "digests": { "md5": "522b0f2e4b572048df2c695a02262893", "sha256": "e5b846e14f56a8a3006e30e9006d2089f3e6650199f9f393cd450fab014592c3" }, "downloads": -1, "filename": "pycalver-201902.24-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "522b0f2e4b572048df2c695a02262893", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 33208, "upload_time": "2019-02-21T16:16:52", "url": "https://files.pythonhosted.org/packages/22/93/59ea49cce3fccfaa228531a72e1ab6ccf796a9165150692faa0d164e629f/pycalver-201902.24-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "076d5339cdd3a2d92a9d8f9baf1431cb", "sha256": "63eb6f587c720d4b78af1ea2f8e0e019b83d312617aa299c11b0def6e3dcb01e" }, "downloads": -1, "filename": "pycalver-201902.24.tar.gz", "has_sig": false, "md5_digest": "076d5339cdd3a2d92a9d8f9baf1431cb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 59852, "upload_time": "2019-02-21T16:16:54", "url": "https://files.pythonhosted.org/packages/8c/4e/78c3a07a2afae6e4bb8ab246d0fa6d389055d77789228d9ee5ae3ae1f1e5/pycalver-201902.24.tar.gz" } ], "201902.27": [ { "comment_text": "", "digests": { "md5": "ba693097fad16201934370572b3c6f14", "sha256": "3433d0266f9f53b716fd3b8bd289f9f57352a5413de4199f92f24aa2b01ed8da" }, "downloads": -1, "filename": "pycalver-201902.27-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ba693097fad16201934370572b3c6f14", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 33830, "upload_time": "2019-02-22T21:51:40", "url": "https://files.pythonhosted.org/packages/a4/ce/2eb5419acb5b47dfc3135f96123fb00ef0aeb4c6a7b38539e393dde396fb/pycalver-201902.27-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2e8f973402736e89380b047e639430e4", "sha256": "05a53697706faf3bf4a9dcdf09b3ee6e1f6abd72753162b155341d05d5a7f036" }, "downloads": -1, "filename": "pycalver-201902.27.tar.gz", "has_sig": false, "md5_digest": "2e8f973402736e89380b047e639430e4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 60701, "upload_time": "2019-02-22T21:51:42", "url": "https://files.pythonhosted.org/packages/91/53/ba139e22bb3713d54142335d1a6a9d62e6201675e1438ad745fdd1c3af4c/pycalver-201902.27.tar.gz" } ], "201903.28": [ { "comment_text": "", "digests": { "md5": "82491b0761f39be35617541baa3b8a88", "sha256": "58dcaef127a1f45c95be7867d655fbdec4e42158c1db3540ec69e28e54807ca6" }, "downloads": -1, "filename": "pycalver-201903.28-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "82491b0761f39be35617541baa3b8a88", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34085, "upload_time": "2019-03-24T18:09:06", "url": "https://files.pythonhosted.org/packages/31/64/ec7fd6c4c4d18ed46c301b5cacde798db9547d4c5bfecde2930f42d96fc0/pycalver-201903.28-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f5d24c1dca230055687e0a738d41b337", "sha256": "eaf0486c5a205cd19bcae7f408299eea2b05f1118e1a1a0a30d19fc696865dc9" }, "downloads": -1, "filename": "pycalver-201903.28.tar.gz", "has_sig": false, "md5_digest": "f5d24c1dca230055687e0a738d41b337", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 61221, "upload_time": "2019-03-24T18:09:08", "url": "https://files.pythonhosted.org/packages/63/af/bddae0ec9237608ea3b671a3ce4d9aa1323f4c97cee23ec2a768670a9a85/pycalver-201903.28.tar.gz" } ], "201903.29b0": [ { "comment_text": "", "digests": { "md5": "47036f7d727b3dd899ccf606d27c529a", "sha256": "383f941eb133456de404ac41d574afc4c803fe0ebd1cd240d0134537ba0a9d2e" }, "downloads": -1, "filename": "pycalver-201903.29b0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "47036f7d727b3dd899ccf606d27c529a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35205, "upload_time": "2019-03-29T01:43:31", "url": "https://files.pythonhosted.org/packages/1a/3e/43a61bab932cf22fbfaaa707464669e1692d182d233cdaba6e38270a43de/pycalver-201903.29b0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "8a9ac00c94ad85efa4f60e8932d92358", "sha256": "35dcb0230453497045ab0b9f23448ba4c25e3d3db5d6599e43cfcf4f136bc992" }, "downloads": -1, "filename": "pycalver-201903.29b0.tar.gz", "has_sig": false, "md5_digest": "8a9ac00c94ad85efa4f60e8932d92358", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 62269, "upload_time": "2019-03-29T01:43:34", "url": "https://files.pythonhosted.org/packages/8a/d1/ec201dcc41c8db1404476cde821722e134b2bf2c15aeb1ad73df4dab2294/pycalver-201903.29b0.tar.gz" } ], "201903.30": [ { "comment_text": "", "digests": { "md5": "9c5facdf86571023f9baf11fcf377141", "sha256": "17bbeb27b632a9b2963662b79ea201867aaea712d889c89de47a9238880b2500" }, "downloads": -1, "filename": "pycalver-201903.30-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9c5facdf86571023f9baf11fcf377141", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35175, "upload_time": "2019-03-29T20:36:12", "url": "https://files.pythonhosted.org/packages/ad/89/94fb1de76442a5e8fc0948c9889c4fabd6f06e97e988ba37a45aaaf25fe2/pycalver-201903.30-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ee9c23a2ac90d6720ad282cc3fee808f", "sha256": "e5563d576b4ea4dd3eac89bfb90d167441d423930010aaffa4ef1548c8500757" }, "downloads": -1, "filename": "pycalver-201903.30.tar.gz", "has_sig": false, "md5_digest": "ee9c23a2ac90d6720ad282cc3fee808f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 62318, "upload_time": "2019-03-29T20:36:15", "url": "https://files.pythonhosted.org/packages/fd/c2/118951387b80b9092759446c1559dd165021413766ac341dc1ba347834a3/pycalver-201903.30.tar.gz" } ], "201907.31b0": [ { "comment_text": "", "digests": { "md5": "e772610615566b2e043b0ee71c8ce70d", "sha256": "7a5508614ed2b2a1b355a3b1f0d9a2e378b3c5398aea4479fc448e7850b512b6" }, "downloads": -1, "filename": "pycalver-201907.31b0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e772610615566b2e043b0ee71c8ce70d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35403, "upload_time": "2019-07-09T18:24:27", "url": "https://files.pythonhosted.org/packages/b4/1f/ca8b2c53ee6d8780dea318ec9081e2d69f1eefb01f78ed8a77c334481848/pycalver-201907.31b0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "4ceb1ae8436204f27e152fd46774b214", "sha256": "e4f797f1be261f97dcdb35d884b14eeb5cbc442c9afd66deef80ccb318f584e2" }, "downloads": -1, "filename": "pycalver-201907.31b0.tar.gz", "has_sig": false, "md5_digest": "4ceb1ae8436204f27e152fd46774b214", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 63054, "upload_time": "2019-07-09T18:24:29", "url": "https://files.pythonhosted.org/packages/43/81/7ede8deafe73e17ac296dbfa5cb1eb0f5301387845f98357075b0d42d619/pycalver-201907.31b0.tar.gz" } ], "201907.32b0": [ { "comment_text": "", "digests": { "md5": "78ef5de7f7b18b40601b88d53079071e", "sha256": "125caa6f195f010d071c86171f02236eccc4a792543fb3e2dcc6bf5181af1566" }, "downloads": -1, "filename": "pycalver-201907.32b0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "78ef5de7f7b18b40601b88d53079071e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35588, "upload_time": "2019-07-25T10:37:23", "url": "https://files.pythonhosted.org/packages/75/b3/51ced85a06f694d5b7ffbd096961d2d4e177df9281ee39eef5e764618c71/pycalver-201907.32b0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b361271d888fc66876afed065362af65", "sha256": "d2ece655390ce879ddfedf610b5f8415f45f74cc9072a9c4215b0ae32286412a" }, "downloads": -1, "filename": "pycalver-201907.32b0.tar.gz", "has_sig": false, "md5_digest": "b361271d888fc66876afed065362af65", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 63645, "upload_time": "2019-07-25T10:37:25", "url": "https://files.pythonhosted.org/packages/32/75/73c3eb7b27c1959d94378bb1bdf4631a776d95241f7ec295b736d6f983bd/pycalver-201907.32b0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "9c5facdf86571023f9baf11fcf377141", "sha256": "17bbeb27b632a9b2963662b79ea201867aaea712d889c89de47a9238880b2500" }, "downloads": -1, "filename": "pycalver-201903.30-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9c5facdf86571023f9baf11fcf377141", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35175, "upload_time": "2019-03-29T20:36:12", "url": "https://files.pythonhosted.org/packages/ad/89/94fb1de76442a5e8fc0948c9889c4fabd6f06e97e988ba37a45aaaf25fe2/pycalver-201903.30-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ee9c23a2ac90d6720ad282cc3fee808f", "sha256": "e5563d576b4ea4dd3eac89bfb90d167441d423930010aaffa4ef1548c8500757" }, "downloads": -1, "filename": "pycalver-201903.30.tar.gz", "has_sig": false, "md5_digest": "ee9c23a2ac90d6720ad282cc3fee808f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 62318, "upload_time": "2019-03-29T20:36:15", "url": "https://files.pythonhosted.org/packages/fd/c2/118951387b80b9092759446c1559dd165021413766ac341dc1ba347834a3/pycalver-201903.30.tar.gz" } ] }