version-query 1.6.1.dev0__tar.gz → 1.6.3.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {version_query-1.6.1.dev0/version_query.egg-info → version_query-1.6.3.dev0}/PKG-INFO +12 -12
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/README.rst +3 -3
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/pyproject.toml +1 -1
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/requirements.txt +1 -1
- version_query-1.6.3.dev0/requirements_test.txt +2 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/setup.py +0 -1
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/test_version.py +8 -5
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/git_query.py +12 -6
- version_query-1.6.3.dev0/version_query/parser.py +53 -0
- version_query-1.6.3.dev0/version_query/patterns.py +39 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/version.py +70 -123
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0/version_query.egg-info}/PKG-INFO +12 -12
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query.egg-info/SOURCES.txt +4 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query.egg-info/requires.txt +1 -1
- version_query-1.6.1.dev0/requirements_test.txt +0 -3
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/LICENSE +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/MANIFEST.in +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/NOTICE +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/setup.cfg +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/__init__.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/examples.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/test_git.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/test_packaging.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/test/test_query.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/__init__.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/__main__.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/_version.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/main.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/py.typed +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/py_query.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query/query.py +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query.egg-info/dependency_links.txt +0 -0
- {version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: version-query
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.3.dev0
|
|
4
4
|
Summary: Zero-overhead package versioning for Python.
|
|
5
5
|
Home-page: https://github.com/mbdevpl/version-query
|
|
6
6
|
Download-URL:
|
|
@@ -8,12 +8,11 @@ Author: Mateusz Bysiek, John Vandenberg
|
|
|
8
8
|
Author-email: mateusz.bysiek@gmail.com
|
|
9
9
|
Maintainer: Mateusz Bysiek
|
|
10
10
|
Maintainer-email: mateusz.bysiek@gmail.com
|
|
11
|
-
License: Apache
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
12
|
Keywords: automation,continuous integration,git,releasing,semantic versioning,tagging,versioning
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
16
|
Classifier: Natural Language :: English
|
|
18
17
|
Classifier: Operating System :: MacOS
|
|
19
18
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -33,7 +32,7 @@ Requires-Python: >=3.9
|
|
|
33
32
|
Description-Content-Type: text/x-rst; charset=UTF-8
|
|
34
33
|
License-File: LICENSE
|
|
35
34
|
License-File: NOTICE
|
|
36
|
-
Requires-Dist: boilerplates[cli,logging]~=1.
|
|
35
|
+
Requires-Dist: boilerplates[cli,logging]~=1.2
|
|
37
36
|
Requires-Dist: GitPython~=3.1
|
|
38
37
|
Requires-Dist: packaging>=24.0
|
|
39
38
|
Requires-Dist: semver<3.1,>=2.13
|
|
@@ -44,7 +43,8 @@ Dynamic: description
|
|
|
44
43
|
Dynamic: description-content-type
|
|
45
44
|
Dynamic: home-page
|
|
46
45
|
Dynamic: keywords
|
|
47
|
-
Dynamic: license
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
Dynamic: license-expression
|
|
48
48
|
Dynamic: maintainer
|
|
49
49
|
Dynamic: maintainer-email
|
|
50
50
|
Dynamic: requires-dist
|
|
@@ -81,7 +81,7 @@ Zero-overhead package versioning for Python.
|
|
|
81
81
|
:alt: grade from Codacy
|
|
82
82
|
|
|
83
83
|
.. image:: https://img.shields.io/github/license/mbdevpl/version-query.svg
|
|
84
|
-
:target: https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
84
|
+
:target: https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/NOTICE
|
|
85
85
|
:alt: license
|
|
86
86
|
|
|
87
87
|
Package versioning toolkit for Python which relies on git tags, git history traversal
|
|
@@ -361,9 +361,9 @@ Incrementing any version component clears all existing following components.
|
|
|
361
361
|
Examples of how version is incremented:
|
|
362
362
|
|
|
363
363
|
* for ``1.5``, incrementing ``<major>`` results in ``2.0``;
|
|
364
|
-
* for ``1.5.1-2.4``, ``<minor
|
|
365
|
-
* ``1.5.1-2.4``, ``<patch
|
|
366
|
-
* ``1.5.1``, ``<major
|
|
364
|
+
* for ``1.5.1-2.4``, ``<minor>`` ++ results in ``1.6``;
|
|
365
|
+
* ``1.5.1-2.4``, ``<patch>`` ++, ``1.5.2``;
|
|
366
|
+
* ``1.5.1``, ``<major>`` += 3, ``4.0.0``.
|
|
367
367
|
|
|
368
368
|
|
|
369
369
|
API details
|
|
@@ -491,8 +491,8 @@ Requirements
|
|
|
491
491
|
|
|
492
492
|
Python version 3.9 or later.
|
|
493
493
|
|
|
494
|
-
Python libraries as specified in `requirements.txt <https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
494
|
+
Python libraries as specified in `requirements.txt <https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/requirements.txt>`_.
|
|
495
495
|
|
|
496
|
-
Building and running tests additionally requires packages listed in `requirements_test.txt <https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
496
|
+
Building and running tests additionally requires packages listed in `requirements_test.txt <https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/requirements_test.txt>`_.
|
|
497
497
|
|
|
498
498
|
Tested on Linux, macOS and Windows.
|
|
@@ -308,9 +308,9 @@ Incrementing any version component clears all existing following components.
|
|
|
308
308
|
Examples of how version is incremented:
|
|
309
309
|
|
|
310
310
|
* for ``1.5``, incrementing ``<major>`` results in ``2.0``;
|
|
311
|
-
* for ``1.5.1-2.4``, ``<minor
|
|
312
|
-
* ``1.5.1-2.4``, ``<patch
|
|
313
|
-
* ``1.5.1``, ``<major
|
|
311
|
+
* for ``1.5.1-2.4``, ``<minor>`` ++ results in ``1.6``;
|
|
312
|
+
* ``1.5.1-2.4``, ``<patch>`` ++, ``1.5.2``;
|
|
313
|
+
* ``1.5.1``, ``<major>`` += 3, ``4.0.0``.
|
|
314
314
|
|
|
315
315
|
|
|
316
316
|
API details
|
|
@@ -15,7 +15,6 @@ class Package(boilerplates.setup.Package):
|
|
|
15
15
|
'Development Status :: 5 - Production/Stable',
|
|
16
16
|
'Environment :: Console',
|
|
17
17
|
'Intended Audience :: Developers',
|
|
18
|
-
'License :: OSI Approved :: Apache Software License',
|
|
19
18
|
'Natural Language :: English',
|
|
20
19
|
'Operating System :: MacOS',
|
|
21
20
|
'Operating System :: Microsoft :: Windows',
|
|
@@ -25,19 +25,21 @@ class Tests(unittest.TestCase):
|
|
|
25
25
|
with self.subTest(version_str=version_str, version_tuple=version_tuple):
|
|
26
26
|
|
|
27
27
|
py_version = \
|
|
28
|
-
pkg_resources.parse_version(version_str)
|
|
29
|
-
# self.assertIsInstance(
|
|
30
|
-
# py_version, packaging.version.Version, msg=(type(py_version), py_version))
|
|
28
|
+
pkg_resources.parse_version(version_str)
|
|
31
29
|
_LOG.debug('packaging parsed version string %s into %s: %s',
|
|
32
30
|
repr(version_str), type(py_version), py_version)
|
|
31
|
+
# self.assertIsInstance(
|
|
32
|
+
# py_version, packaging.version.Version, msg=(type(py_version), py_version))
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
|
-
sem_version = semver.VersionInfo.parse(version_str)
|
|
35
|
+
sem_version = semver.VersionInfo.parse(version_str)
|
|
36
36
|
except ValueError:
|
|
37
37
|
_LOG.debug('semver could not parse version string %s', repr(version_str))
|
|
38
38
|
else:
|
|
39
39
|
_LOG.debug('semver parsed version string %s into %s: %s',
|
|
40
40
|
repr(version_str), type(sem_version), sem_version)
|
|
41
|
+
# self.assertIsInstance(
|
|
42
|
+
# sem_version, semver.VersionInfo, msg=(type(sem_version), sem_version))
|
|
41
43
|
|
|
42
44
|
self.assertEqual(Version.from_str(version_str).to_tuple(), version_tuple)
|
|
43
45
|
|
|
@@ -76,7 +78,7 @@ class Tests(unittest.TestCase):
|
|
|
76
78
|
version_tuple = case_to_version_tuple(args, kwargs)
|
|
77
79
|
with self.subTest(version_str=version_str, version_tuple=version_tuple):
|
|
78
80
|
try:
|
|
79
|
-
sem_version = semver.VersionInfo.parse(version_str)
|
|
81
|
+
sem_version = semver.VersionInfo.parse(version_str)
|
|
80
82
|
except ValueError:
|
|
81
83
|
continue
|
|
82
84
|
else:
|
|
@@ -123,6 +125,7 @@ class Tests(unittest.TestCase):
|
|
|
123
125
|
self.assertIsInstance(version.local, tuple)
|
|
124
126
|
|
|
125
127
|
def test_init_bad(self):
|
|
128
|
+
# pylint: disable = redefined-variable-type
|
|
126
129
|
for (args, kwargs), exception in BAD_INIT_CASES.items():
|
|
127
130
|
with self.subTest(args=args, kwargs=kwargs, exception=exception):
|
|
128
131
|
with self.assertRaises(exception):
|
|
@@ -40,6 +40,17 @@ def _git_version_tags(repo: git.Repo) -> t.Mapping[git.Tag, Version]:
|
|
|
40
40
|
return versions
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def _git_version_tag_commits(
|
|
44
|
+
version_tags: t.Iterable[git.Tag]) -> t.Mapping[git.objects.Commit, t.Set[git.Tag]]:
|
|
45
|
+
version_tag_commits: t.Dict[git.objects.Commit, t.Set[git.Tag]] = {}
|
|
46
|
+
for tag in version_tags:
|
|
47
|
+
_commit = tag.commit
|
|
48
|
+
if _commit not in version_tag_commits:
|
|
49
|
+
version_tag_commits[_commit] = set()
|
|
50
|
+
version_tag_commits[_commit].add(tag)
|
|
51
|
+
return version_tag_commits
|
|
52
|
+
|
|
53
|
+
|
|
43
54
|
def _latest_git_version_tag_on_branches(
|
|
44
55
|
repo: git.Repo, assume_if_none: bool, commit: git.objects.Commit, commit_distance: int,
|
|
45
56
|
skip_commits: t.Set[git.objects.Commit]) -> t.Union[int, t.Tuple[
|
|
@@ -80,12 +91,7 @@ def _latest_git_version_tag(
|
|
|
80
91
|
t.Optional[git.objects.Commit], t.Optional[git.TagReference], t.Optional[Version], int]:
|
|
81
92
|
"""Return (commit, tag at that commit if any, latest version, distance from the version)."""
|
|
82
93
|
version_tags = _git_version_tags(repo)
|
|
83
|
-
version_tag_commits
|
|
84
|
-
for tag, version in version_tags.items():
|
|
85
|
-
_commit = tag.commit
|
|
86
|
-
if _commit not in version_tag_commits:
|
|
87
|
-
version_tag_commits[_commit] = set()
|
|
88
|
-
version_tag_commits[_commit].add(tag)
|
|
94
|
+
version_tag_commits = _git_version_tag_commits(version_tags.keys())
|
|
89
95
|
current_version_tags = {}
|
|
90
96
|
commit = None
|
|
91
97
|
if skip_commits is None:
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Functions for parsing version strings into their components and verifying the format."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import typing as t
|
|
5
|
+
|
|
6
|
+
from . import patterns
|
|
7
|
+
|
|
8
|
+
_LOG = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_release_str(release: str) -> tuple:
|
|
12
|
+
"""Parse a release string into major, minor, and patch version numbers."""
|
|
13
|
+
match = patterns.RELEASE.fullmatch(release)
|
|
14
|
+
assert match is not None
|
|
15
|
+
major_match = match.group('major')
|
|
16
|
+
assert major_match is not None
|
|
17
|
+
major = int(major_match)
|
|
18
|
+
minor_match = match.group('minor')
|
|
19
|
+
if minor_match is not None:
|
|
20
|
+
minor = int(minor_match)
|
|
21
|
+
else:
|
|
22
|
+
minor = None
|
|
23
|
+
patch_match = match.group('patch')
|
|
24
|
+
if patch_match is not None:
|
|
25
|
+
patch = int(patch_match)
|
|
26
|
+
else:
|
|
27
|
+
patch = None
|
|
28
|
+
return major, minor, patch
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_pre_release_str(pre_release: str) -> t.Sequence[
|
|
32
|
+
t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]:
|
|
33
|
+
"""Parse a pre-release string into a sequence of tuples."""
|
|
34
|
+
parts = patterns.PRE_RELEASE.findall(pre_release)
|
|
35
|
+
_LOG.debug('parsed pre-release string %s into %s', repr(pre_release), parts)
|
|
36
|
+
tuples = []
|
|
37
|
+
for part in parts:
|
|
38
|
+
match = patterns.PRE_RELEASE_PART.fullmatch(part)
|
|
39
|
+
assert match is not None
|
|
40
|
+
pre_patch_match = match.group('prepatch')
|
|
41
|
+
if pre_patch_match is not None:
|
|
42
|
+
pre_patch = int(pre_patch_match)
|
|
43
|
+
else:
|
|
44
|
+
pre_patch = None
|
|
45
|
+
tuples.append((match.group('preseparator'), match.group('pretype'), pre_patch))
|
|
46
|
+
return tuples
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_local_str(local: str) -> tuple:
|
|
50
|
+
"""Parse a local version suffix string into a sequence."""
|
|
51
|
+
match = patterns.LOCAL.fullmatch(local)
|
|
52
|
+
assert match is not None
|
|
53
|
+
return tuple(_ for _ in match.groups() if _ is not None)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Patterns for recognising parts of version strings and parsing them."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
# pylint: disable = consider-using-f-string
|
|
6
|
+
|
|
7
|
+
_NUMBER = r'(?:0|[123456789][0123456789]*)'
|
|
8
|
+
# _SHA = r'[0123456789abcdef]+'
|
|
9
|
+
_LETTERS = r'(?:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+)'
|
|
10
|
+
LETTERS = re.compile(_LETTERS)
|
|
11
|
+
_ALPHANUMERIC = r'(?:[0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+)'
|
|
12
|
+
ALPHANUMERIC = re.compile(_ALPHANUMERIC)
|
|
13
|
+
_SEP = r'(?:[\.-])'
|
|
14
|
+
|
|
15
|
+
_RELEASE_PARTS = \
|
|
16
|
+
r'(?P<major>{n})(?:\.(?P<minor>{n}))?(?:\.(?P<patch>{n}))?'.format(n=_NUMBER)
|
|
17
|
+
RELEASE = re.compile(_RELEASE_PARTS)
|
|
18
|
+
|
|
19
|
+
_PRE_SEPARATOR = rf'(?P<preseparator>{_SEP})'
|
|
20
|
+
_PRE_TYPE = rf'(?P<pretype>{_LETTERS})'
|
|
21
|
+
_PRE_PATCH = rf'(?P<prepatch>{_NUMBER})'
|
|
22
|
+
_PRE_RELEASE_PART = rf'{_PRE_SEPARATOR}?{_PRE_TYPE}?{_PRE_PATCH}?'
|
|
23
|
+
PRE_RELEASE_PART = re.compile(_PRE_RELEASE_PART)
|
|
24
|
+
_PRE_RELEASE_PARTS = r'(?:{0}{2})|(?:{0}?{1}{2}?)'.format(_SEP, _LETTERS, _NUMBER)
|
|
25
|
+
PRE_RELEASE = re.compile(_PRE_RELEASE_PARTS)
|
|
26
|
+
# PRE_RELEASE_CHECK = re.compile(rf'(?:{_PRE_RELEASE_PARTS})+')
|
|
27
|
+
|
|
28
|
+
_LOCAL_SEPARATOR = rf'({_SEP})'
|
|
29
|
+
_LOCAL_PART = rf'({_ALPHANUMERIC})'
|
|
30
|
+
_LOCAL_PARTS = rf'\+{_LOCAL_PART}(?:{_LOCAL_SEPARATOR}{_LOCAL_PART})*'
|
|
31
|
+
LOCAL = re.compile(_LOCAL_PARTS)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_RELEASE = r'(?P<release>{n}(?:\.{n})?(?:\.{n})?)'.format(n=_NUMBER)
|
|
35
|
+
_PRE_RELEASE = r'(?P<prerelease>(?:(?:{0}{2})|(?:{0}?{1}{2}?))+)'.format(_SEP, _LETTERS, _NUMBER)
|
|
36
|
+
_LOCAL = r'(?P<local>\+{0}([\.-]{0})*)'.format(_ALPHANUMERIC)
|
|
37
|
+
# _NAMED_PARTS_COUNT = 3 + 3
|
|
38
|
+
_VERSION = rf'{_RELEASE}{_PRE_RELEASE}?{_LOCAL}?'
|
|
39
|
+
VERSION = re.compile(_VERSION)
|
|
@@ -4,12 +4,14 @@ import collections.abc
|
|
|
4
4
|
import enum
|
|
5
5
|
import itertools
|
|
6
6
|
import logging
|
|
7
|
-
import re
|
|
8
7
|
import typing as t
|
|
9
8
|
|
|
10
9
|
import packaging.version
|
|
11
10
|
import semver
|
|
12
11
|
|
|
12
|
+
from . import patterns
|
|
13
|
+
from .parser import parse_release_str, parse_pre_release_str, parse_local_str
|
|
14
|
+
|
|
13
15
|
_LOG = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
PY_PRE_RELEASE_INDICATORS = {'a', 'b', 'c', 'rc'}
|
|
@@ -19,6 +21,8 @@ PY_PRE_RELEASE_INDICATORS = {'a', 'b', 'c', 'rc'}
|
|
|
19
21
|
class VersionComponent(enum.IntEnum):
|
|
20
22
|
"""Enumeration of standard version components."""
|
|
21
23
|
|
|
24
|
+
# pylint: disable = invalid-name
|
|
25
|
+
|
|
22
26
|
Major = 1 << 1
|
|
23
27
|
Minor = 1 << 2
|
|
24
28
|
Patch = 1 << 3
|
|
@@ -29,96 +33,21 @@ class VersionComponent(enum.IntEnum):
|
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
def _version_tuple_checker(version_tuple, flags):
|
|
32
|
-
return all(
|
|
36
|
+
return all((_ is not None if flag else _ is None) for _, flag in zip(version_tuple, flags))
|
|
33
37
|
|
|
34
38
|
|
|
35
|
-
class Version(collections.abc.Hashable):
|
|
39
|
+
class Version(collections.abc.Hashable):
|
|
36
40
|
"""For storing and manipulating version information.
|
|
37
41
|
|
|
38
42
|
Definitions of acceptable version formats are provided in readme.
|
|
39
43
|
"""
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
# _re_sha = r'[0123456789abcdef]+'
|
|
43
|
-
_re_letters = r'(?:[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+)'
|
|
44
|
-
_pattern_letters = re.compile(_re_letters)
|
|
45
|
-
_re_alphanumeric = r'(?:[0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+)'
|
|
46
|
-
_pattern_alphanumeric = re.compile(_re_alphanumeric)
|
|
47
|
-
_re_sep = r'(?:[\.-])'
|
|
48
|
-
|
|
49
|
-
_re_release_parts = \
|
|
50
|
-
r'(?P<major>{n})(?:\.(?P<minor>{n}))?(?:\.(?P<patch>{n}))?'.format(n=_re_number)
|
|
51
|
-
_pattern_release = re.compile(_re_release_parts)
|
|
52
|
-
|
|
53
|
-
@classmethod
|
|
54
|
-
def _parse_release_str(cls, release: str) -> tuple:
|
|
55
|
-
match = cls._pattern_release.fullmatch(release)
|
|
56
|
-
assert match is not None
|
|
57
|
-
major_match = match.group('major')
|
|
58
|
-
assert major_match is not None
|
|
59
|
-
major = int(major_match)
|
|
60
|
-
minor_match = match.group('minor')
|
|
61
|
-
if minor_match is not None:
|
|
62
|
-
minor = int(minor_match)
|
|
63
|
-
else:
|
|
64
|
-
minor = None
|
|
65
|
-
patch_match = match.group('patch')
|
|
66
|
-
if patch_match is not None:
|
|
67
|
-
patch = int(patch_match)
|
|
68
|
-
else:
|
|
69
|
-
patch = None
|
|
70
|
-
return major, minor, patch
|
|
71
|
-
|
|
72
|
-
_re_pre_separator = rf'(?P<preseparator>{_re_sep})'
|
|
73
|
-
_re_pre_type = rf'(?P<pretype>{_re_letters})'
|
|
74
|
-
_re_pre_patch = rf'(?P<prepatch>{_re_number})'
|
|
75
|
-
_re_pre_release_part = rf'{_re_pre_separator}?{_re_pre_type}?{_re_pre_patch}?'
|
|
76
|
-
_pattern_pre_release_part = re.compile(_re_pre_release_part)
|
|
77
|
-
_re_pre_release_parts = r'(?:{0}{2})|(?:{0}?{1}{2}?)'.format(_re_sep, _re_letters, _re_number)
|
|
78
|
-
_pattern_pre_release = re.compile(_re_pre_release_parts)
|
|
79
|
-
_pattern_pre_release_check = re.compile(rf'(?:{_re_pre_release_parts})+')
|
|
80
|
-
|
|
81
|
-
@classmethod
|
|
82
|
-
def _parse_pre_release_str(cls, pre_release: str) -> t.Sequence[
|
|
83
|
-
t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]:
|
|
84
|
-
parts = cls._pattern_pre_release.findall(pre_release)
|
|
85
|
-
_LOG.debug('parsed pre-release string %s into %s',
|
|
86
|
-
repr(pre_release), parts)
|
|
87
|
-
tuples = []
|
|
88
|
-
for part in parts:
|
|
89
|
-
match = cls._pattern_pre_release_part.fullmatch(part)
|
|
90
|
-
assert match is not None
|
|
91
|
-
pre_patch_match = match.group('prepatch')
|
|
92
|
-
if pre_patch_match is not None:
|
|
93
|
-
pre_patch = int(pre_patch_match)
|
|
94
|
-
else:
|
|
95
|
-
pre_patch = None
|
|
96
|
-
tuples.append((match.group('preseparator'), match.group('pretype'), pre_patch))
|
|
97
|
-
return tuples
|
|
98
|
-
|
|
99
|
-
_re_local_separator = rf'({_re_sep})'
|
|
100
|
-
_re_local_part = rf'({_re_alphanumeric})'
|
|
101
|
-
_re_local_parts = rf'\+{_re_local_part}(?:{_re_local_separator}{_re_local_part})*'
|
|
102
|
-
_pattern_local = re.compile(_re_local_parts)
|
|
103
|
-
|
|
104
|
-
@classmethod
|
|
105
|
-
def _parse_local_str(cls, local: str) -> tuple:
|
|
106
|
-
match = cls._pattern_local.fullmatch(local)
|
|
107
|
-
assert match is not None
|
|
108
|
-
return tuple([_ for _ in match.groups() if _ is not None])
|
|
109
|
-
|
|
110
|
-
_re_release = r'(?P<release>{n}(?:\.{n})?(?:\.{n})?)'.format(n=_re_number)
|
|
111
|
-
_re_pre_release = r'(?P<prerelease>(?:(?:{0}{2})|(?:{0}?{1}{2}?))+)'.format(
|
|
112
|
-
_re_sep, _re_letters, _re_number)
|
|
113
|
-
_re_local = r'(?P<local>\+{0}([\.-]{0})*)'.format(_re_alphanumeric)
|
|
114
|
-
# _re_named_parts_count = 3 + 3
|
|
115
|
-
_re_version = rf'{_re_release}{_re_pre_release}?{_re_local}?'
|
|
116
|
-
_pattern_version = re.compile(_re_version)
|
|
45
|
+
# pylint: disable = too-many-public-methods, too-many-instance-attributes
|
|
117
46
|
|
|
118
47
|
@classmethod
|
|
119
48
|
def from_str(cls, version_str: str):
|
|
120
49
|
"""Create version from string."""
|
|
121
|
-
match =
|
|
50
|
+
match = patterns.VERSION.fullmatch(version_str) # type: t.Optional[t.Match[str]]
|
|
122
51
|
if match is None:
|
|
123
52
|
raise ValueError(f'version string {repr(version_str)} is invalid')
|
|
124
53
|
_LOG.debug('version_query parsed version string %s into %s: %s %s',
|
|
@@ -128,9 +57,9 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
128
57
|
_pre_release = match.group('prerelease')
|
|
129
58
|
_local = match.group('local')
|
|
130
59
|
|
|
131
|
-
major, minor, patch =
|
|
132
|
-
pre_release = None if _pre_release is None else
|
|
133
|
-
local = None if _local is None else
|
|
60
|
+
major, minor, patch = parse_release_str(_release)
|
|
61
|
+
pre_release = None if _pre_release is None else parse_pre_release_str(_pre_release)
|
|
62
|
+
local = None if _local is None else parse_local_str(_local)
|
|
134
63
|
|
|
135
64
|
return cls(major=major, minor=minor, patch=patch, pre_release=pre_release, local=local)
|
|
136
65
|
|
|
@@ -147,13 +76,13 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
147
76
|
"""Create version from a standard Python version object."""
|
|
148
77
|
if not isinstance(py_version, packaging.version.Version):
|
|
149
78
|
_LOG.warning('attempting to parse %s as packaging.version.Version...', type(py_version))
|
|
150
|
-
ver = py_version._version
|
|
79
|
+
ver = py_version._version # pylint: disable = protected-access
|
|
151
80
|
major, minor, patch = [ver.release[i] if len(ver.release) > i
|
|
152
81
|
else None for i in range(3)]
|
|
153
82
|
pre_release: t.Optional[t.Sequence[
|
|
154
83
|
t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]] = None
|
|
155
84
|
local = None
|
|
156
|
-
pre_ver: t.Optional[t.Tuple[None, int]]
|
|
85
|
+
pre_ver: t.Optional[t.Tuple[str | None, int]]
|
|
157
86
|
if len(ver.release) == 4:
|
|
158
87
|
pre_ver = (None, ver.release[3])
|
|
159
88
|
elif len(ver.release) > 4:
|
|
@@ -176,12 +105,14 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
176
105
|
local = tuple(itertools.chain.from_iterable(
|
|
177
106
|
(dot, str(_)) for dot, _ in zip('.' * len(ver.local), ver.local)))[1:]
|
|
178
107
|
_LOG.debug('parsing %s %s', type(py_version), py_version)
|
|
108
|
+
assert major is not None
|
|
179
109
|
return cls(major, minor, patch, pre_release=pre_release, local=local)
|
|
180
110
|
|
|
181
111
|
@classmethod
|
|
182
112
|
def from_sem_version(cls, sem_version: t.Union[dict, semver.VersionInfo]):
|
|
183
113
|
"""Create version from semantic version object."""
|
|
184
114
|
_LOG.debug('parsing %s %s', type(sem_version), sem_version)
|
|
115
|
+
local: str | tuple | None
|
|
185
116
|
if isinstance(sem_version, semver.VersionInfo):
|
|
186
117
|
major, minor, patch = sem_version.major, sem_version.minor, sem_version.patch
|
|
187
118
|
pre_release = sem_version.prerelease
|
|
@@ -193,24 +124,24 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
193
124
|
if pre_release is not None:
|
|
194
125
|
raise NotImplementedError(sem_version)
|
|
195
126
|
if local is not None:
|
|
196
|
-
local =
|
|
127
|
+
local = parse_local_str(f'+{local}')
|
|
197
128
|
return cls(major, minor, patch, pre_release=pre_release, local=local)
|
|
198
129
|
|
|
199
130
|
@classmethod
|
|
200
131
|
def from_version(cls, version: 'Version'):
|
|
201
132
|
return cls.from_dict(version.to_dict())
|
|
202
133
|
|
|
203
|
-
def __init__(
|
|
134
|
+
def __init__( # pylint: disable = keyword-arg-before-vararg, too-many-arguments
|
|
204
135
|
self, major: int, minor: t.Optional[int] = None, patch: t.Optional[int] = None, *args,
|
|
205
|
-
pre_release: t.Sequence[
|
|
206
|
-
t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]] = None,
|
|
207
|
-
local: t.Union[str, tuple] = None):
|
|
136
|
+
pre_release: t.Optional[t.Sequence[
|
|
137
|
+
t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]] = None,
|
|
138
|
+
local: t.Union[str, tuple, None] = None):
|
|
208
139
|
self._major: t.Optional[int] = None
|
|
209
140
|
self._minor: t.Optional[int] = None
|
|
210
141
|
self._patch: t.Optional[int] = None
|
|
211
142
|
self._pre_release: t.Optional[
|
|
212
143
|
t.List[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]] = None
|
|
213
|
-
self._local: t.Optional[t.
|
|
144
|
+
self._local: t.Optional[t.Tuple[str, ...]] = None
|
|
214
145
|
|
|
215
146
|
self.release = major, minor, patch
|
|
216
147
|
|
|
@@ -224,7 +155,7 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
224
155
|
args = args[consumed_args:]
|
|
225
156
|
else:
|
|
226
157
|
pre_release = None
|
|
227
|
-
self.pre_release = pre_release
|
|
158
|
+
self.pre_release = pre_release if pre_release is None else list(pre_release)
|
|
228
159
|
|
|
229
160
|
if args and local is not None:
|
|
230
161
|
raise ValueError(f'args={args} and local={local} are present at the same time'
|
|
@@ -243,35 +174,51 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
243
174
|
|
|
244
175
|
def _get_pre_release_from_args(self, args) -> t.Tuple[
|
|
245
176
|
t.Sequence[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]], int]:
|
|
177
|
+
"""Get pre-release version component from args."""
|
|
178
|
+
if args and isinstance(args[0], tuple):
|
|
179
|
+
return self._get_prerelease_from_tuples(args)
|
|
180
|
+
|
|
246
181
|
pre_release: t.List[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]] = []
|
|
247
182
|
consumed_args = 0
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
183
|
+
accumulated: t.List[t.Union[int, str, None]] = []
|
|
184
|
+
for i, arg in enumerate(args):
|
|
185
|
+
if not accumulated:
|
|
186
|
+
if arg in (None, '.', '-'):
|
|
187
|
+
if len(args) < i + 3:
|
|
188
|
+
raise ValueError(
|
|
189
|
+
'expected 3 consecutive values'
|
|
190
|
+
f' from index {i} in args={args} in {repr(self)}')
|
|
191
|
+
else:
|
|
257
192
|
break
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
193
|
+
accumulated.append(arg)
|
|
194
|
+
consumed_args += 1
|
|
195
|
+
if len(accumulated) == 3:
|
|
196
|
+
_separator, _type, _patch = \
|
|
197
|
+
accumulated # pylint: disable = unbalanced-tuple-unpacking
|
|
198
|
+
assert _separator is None or isinstance(_separator, str), _separator
|
|
199
|
+
assert _type is None or isinstance(_type, str), _type
|
|
200
|
+
assert _patch is None or isinstance(_patch, int), _patch
|
|
201
|
+
pre_release.append((_separator, _type, _patch))
|
|
202
|
+
accumulated = []
|
|
203
|
+
return pre_release, consumed_args
|
|
204
|
+
|
|
205
|
+
def _get_prerelease_from_tuples(self, args) -> t.Tuple[
|
|
206
|
+
t.Sequence[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]], int]:
|
|
207
|
+
"""Get pre-release version component from args, assuming they are sequence of tuples."""
|
|
208
|
+
pre_release: t.List[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]] = []
|
|
209
|
+
consumed_args = 0
|
|
210
|
+
for i, arg in enumerate(args):
|
|
211
|
+
if not isinstance(arg, tuple):
|
|
212
|
+
break
|
|
213
|
+
if len(arg) == 3 and arg[0] in (None, '.', '-'):
|
|
214
|
+
pre_release.append(arg)
|
|
271
215
|
consumed_args += 1
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
216
|
+
continue
|
|
217
|
+
if i == len(args) - 1:
|
|
218
|
+
break
|
|
219
|
+
raise ValueError(
|
|
220
|
+
f'pre-release segment arg={arg} (index {i} in args={args} in {repr(self)})'
|
|
221
|
+
' must be a 3-tuple')
|
|
275
222
|
return pre_release, consumed_args
|
|
276
223
|
|
|
277
224
|
@property
|
|
@@ -312,7 +259,7 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
312
259
|
|
|
313
260
|
@property
|
|
314
261
|
def pre_release(self) -> t.Optional[
|
|
315
|
-
t.
|
|
262
|
+
t.List[t.Tuple[t.Optional[str], t.Optional[str], t.Optional[int]]]]:
|
|
316
263
|
"""Pre-release version component."""
|
|
317
264
|
if self._pre_release is None:
|
|
318
265
|
return None
|
|
@@ -354,7 +301,7 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
354
301
|
if pre_type is not None and not isinstance(pre_type, str):
|
|
355
302
|
raise TypeError(
|
|
356
303
|
f'pre_type={repr(pre_type)} is of wrong type {type(pre_type)} in {repr(self)}')
|
|
357
|
-
if pre_type is not None and
|
|
304
|
+
if pre_type is not None and patterns.LETTERS.fullmatch(pre_type) is None:
|
|
358
305
|
raise ValueError(f'pre_type={repr(pre_type)} has wrong value in {repr(self)}')
|
|
359
306
|
if pre_patch is not None and not isinstance(pre_patch, int):
|
|
360
307
|
raise TypeError(
|
|
@@ -393,7 +340,7 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
393
340
|
raise TypeError(f'local_part or local_separator {repr(part)} is of wrong type'
|
|
394
341
|
f' {type(part)} in {repr(self)}')
|
|
395
342
|
if i % 2 == 0:
|
|
396
|
-
if
|
|
343
|
+
if patterns.ALPHANUMERIC.fullmatch(part) is None:
|
|
397
344
|
raise ValueError(f'local_part={repr(part)} has wrong value in {repr(self)}')
|
|
398
345
|
elif part not in ('-', '.'):
|
|
399
346
|
raise ValueError(f'local_separator={repr(part)} has wrong value in {repr(self)}')
|
|
@@ -501,13 +448,13 @@ class Version(collections.abc.Hashable): # pylint: disable = too-many-public-me
|
|
|
501
448
|
assert self._pre_release is not None
|
|
502
449
|
version_tuple = self._pre_release[segment]
|
|
503
450
|
if _version_tuple_checker(version_tuple, (True, True, False)):
|
|
504
|
-
return '{}{}'.format(*version_tuple[:2])
|
|
451
|
+
return '{}{}'.format(*version_tuple[:2]) # pylint: disable = consider-using-f-string
|
|
505
452
|
if _version_tuple_checker(version_tuple, (True, False, True)):
|
|
506
453
|
return f'{version_tuple[0]}{version_tuple[2]}'
|
|
507
454
|
if _version_tuple_checker(version_tuple, (False, True, True)):
|
|
508
|
-
return '{}{}'.format(*version_tuple[1:])
|
|
455
|
+
return '{}{}'.format(*version_tuple[1:]) # pylint: disable = consider-using-f-string
|
|
509
456
|
if _version_tuple_checker(version_tuple, (True, True, True)):
|
|
510
|
-
return '{}{}{}'.format(*version_tuple)
|
|
457
|
+
return '{}{}{}'.format(*version_tuple) # pylint: disable = consider-using-f-string
|
|
511
458
|
raise ValueError(f'cannot generate valid version string from {repr(self)}')
|
|
512
459
|
|
|
513
460
|
def pre_release_to_str(self) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: version-query
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.3.dev0
|
|
4
4
|
Summary: Zero-overhead package versioning for Python.
|
|
5
5
|
Home-page: https://github.com/mbdevpl/version-query
|
|
6
6
|
Download-URL:
|
|
@@ -8,12 +8,11 @@ Author: Mateusz Bysiek, John Vandenberg
|
|
|
8
8
|
Author-email: mateusz.bysiek@gmail.com
|
|
9
9
|
Maintainer: Mateusz Bysiek
|
|
10
10
|
Maintainer-email: mateusz.bysiek@gmail.com
|
|
11
|
-
License: Apache
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
12
|
Keywords: automation,continuous integration,git,releasing,semantic versioning,tagging,versioning
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: Console
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
16
|
Classifier: Natural Language :: English
|
|
18
17
|
Classifier: Operating System :: MacOS
|
|
19
18
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -33,7 +32,7 @@ Requires-Python: >=3.9
|
|
|
33
32
|
Description-Content-Type: text/x-rst; charset=UTF-8
|
|
34
33
|
License-File: LICENSE
|
|
35
34
|
License-File: NOTICE
|
|
36
|
-
Requires-Dist: boilerplates[cli,logging]~=1.
|
|
35
|
+
Requires-Dist: boilerplates[cli,logging]~=1.2
|
|
37
36
|
Requires-Dist: GitPython~=3.1
|
|
38
37
|
Requires-Dist: packaging>=24.0
|
|
39
38
|
Requires-Dist: semver<3.1,>=2.13
|
|
@@ -44,7 +43,8 @@ Dynamic: description
|
|
|
44
43
|
Dynamic: description-content-type
|
|
45
44
|
Dynamic: home-page
|
|
46
45
|
Dynamic: keywords
|
|
47
|
-
Dynamic: license
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
Dynamic: license-expression
|
|
48
48
|
Dynamic: maintainer
|
|
49
49
|
Dynamic: maintainer-email
|
|
50
50
|
Dynamic: requires-dist
|
|
@@ -81,7 +81,7 @@ Zero-overhead package versioning for Python.
|
|
|
81
81
|
:alt: grade from Codacy
|
|
82
82
|
|
|
83
83
|
.. image:: https://img.shields.io/github/license/mbdevpl/version-query.svg
|
|
84
|
-
:target: https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
84
|
+
:target: https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/NOTICE
|
|
85
85
|
:alt: license
|
|
86
86
|
|
|
87
87
|
Package versioning toolkit for Python which relies on git tags, git history traversal
|
|
@@ -361,9 +361,9 @@ Incrementing any version component clears all existing following components.
|
|
|
361
361
|
Examples of how version is incremented:
|
|
362
362
|
|
|
363
363
|
* for ``1.5``, incrementing ``<major>`` results in ``2.0``;
|
|
364
|
-
* for ``1.5.1-2.4``, ``<minor
|
|
365
|
-
* ``1.5.1-2.4``, ``<patch
|
|
366
|
-
* ``1.5.1``, ``<major
|
|
364
|
+
* for ``1.5.1-2.4``, ``<minor>`` ++ results in ``1.6``;
|
|
365
|
+
* ``1.5.1-2.4``, ``<patch>`` ++, ``1.5.2``;
|
|
366
|
+
* ``1.5.1``, ``<major>`` += 3, ``4.0.0``.
|
|
367
367
|
|
|
368
368
|
|
|
369
369
|
API details
|
|
@@ -491,8 +491,8 @@ Requirements
|
|
|
491
491
|
|
|
492
492
|
Python version 3.9 or later.
|
|
493
493
|
|
|
494
|
-
Python libraries as specified in `requirements.txt <https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
494
|
+
Python libraries as specified in `requirements.txt <https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/requirements.txt>`_.
|
|
495
495
|
|
|
496
|
-
Building and running tests additionally requires packages listed in `requirements_test.txt <https://github.com/mbdevpl/version-query/blob/v1.6.
|
|
496
|
+
Building and running tests additionally requires packages listed in `requirements_test.txt <https://github.com/mbdevpl/version-query/blob/v1.6.3.dev0/requirements_test.txt>`_.
|
|
497
497
|
|
|
498
498
|
Tested on Linux, macOS and Windows.
|
|
@@ -17,6 +17,8 @@ setup.py
|
|
|
17
17
|
./version_query/_version.py
|
|
18
18
|
./version_query/git_query.py
|
|
19
19
|
./version_query/main.py
|
|
20
|
+
./version_query/parser.py
|
|
21
|
+
./version_query/patterns.py
|
|
20
22
|
./version_query/py.typed
|
|
21
23
|
./version_query/py_query.py
|
|
22
24
|
./version_query/query.py
|
|
@@ -32,6 +34,8 @@ version_query/__main__.py
|
|
|
32
34
|
version_query/_version.py
|
|
33
35
|
version_query/git_query.py
|
|
34
36
|
version_query/main.py
|
|
37
|
+
version_query/parser.py
|
|
38
|
+
version_query/patterns.py
|
|
35
39
|
version_query/py.typed
|
|
36
40
|
version_query/py_query.py
|
|
37
41
|
version_query/query.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{version_query-1.6.1.dev0 → version_query-1.6.3.dev0}/version_query.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|