From 1c007d24f2733efac6ea5e6eda5e9b0319c38bf5 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 08:35:10 +0300 Subject: [PATCH 1/8] Add setuptools.get_version(path, field='__version__') This allows people who use setuptools to maintain project versions in a single place, for example as attributes in their Python source. Closes #1316 Closes https://github.com/pypa/pip/pull/6004 Closes https://github.com/pypa/python-packaging-user-guide/pull/571 --- setup.cfg | 2 -- setup.py | 2 +- setuptools/__init__.py | 22 +++++++++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index f0932e1c86..993ef6b08d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,5 +25,3 @@ universal = 1 [metadata] license_file = LICENSE -[bumpversion:file:setup.py] - diff --git a/setup.py b/setup.py index 8ec7ce06e5..4c9f2055e1 100755 --- a/setup.py +++ b/setup.py @@ -89,7 +89,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="40.8.0", + version=setuptools.get_version("setup.cfg", field="current_version"), description=( "Easily download, build, install, upgrade, and uninstall " "Python packages" diff --git a/setuptools/__init__.py b/setuptools/__init__.py index a71b2bbdc6..d405ef1e5e 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -27,7 +27,7 @@ __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', 'SetuptoolsDeprecationWarning', - 'find_packages' + 'find_packages', 'get_version' ] if PY3: @@ -224,5 +224,25 @@ def findall(dir=os.curdir): return list(files) +def get_version(path, field='__version__'): + """ + Get version information from a text file. Version is extracted + from "key = value" format with key matched at the beginning of + a line and specified by `field` parameter. Returns string. + """ + version_file = os.path.abspath(path) + # Using binary matching to avoid possible encoding problems + # when reading arbitrary text files + if type(field) is not bytes: + field = field.encode('utf-8') + for line in open(version_file, 'rb'): + if line.startswith(field): + # __version__ = "0.9" + # current_version = 4.8.0 + _, value = line.split(b'=') + version = value.strip(b' \'\"').decode() + return version + + # Apply monkey patches monkey.patch_all() From 958cbc166e748a522393effa2746a2bc11cb7dc1 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 09:26:42 +0300 Subject: [PATCH 2/8] Add news fragment --- changelog.d/1679.change.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/1679.change.rst diff --git a/changelog.d/1679.change.rst b/changelog.d/1679.change.rst new file mode 100644 index 0000000000..05fc148239 --- /dev/null +++ b/changelog.d/1679.change.rst @@ -0,0 +1,3 @@ +Add ``setuptools.get_version(path, field='__version__')``. It extracts version +info from lines like ``__version__ = '0.12'"`` in text files and Python +sources without importing them. From 31f61319b4159145506340bc1b7e84e18eab7e75 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 10:15:27 +0300 Subject: [PATCH 3/8] Tests for setuptools.get_version() --- setuptools/tests/test_get_version.py | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 setuptools/tests/test_get_version.py diff --git a/setuptools/tests/test_get_version.py b/setuptools/tests/test_get_version.py new file mode 100644 index 0000000000..ca48f6c80e --- /dev/null +++ b/setuptools/tests/test_get_version.py @@ -0,0 +1,40 @@ +# -*- encoding: utf-8 -*- + +"""Tests for setuptools.get_version().""" +import os +import codecs +import tempfile + +import pytest + +from setuptools import __version__, get_version + + +def test_get_version(): + version = get_version('setup.cfg', field='current_version') + assert version == __version__ + + +class TestFiles: + def setup_method(self, method): + self.tmpdir = tempfile.mkdtemp() + + self.file_python = os.path.join(self.tmpdir, 'version.py') + with open(self.file_python, 'w') as fp: + fp.write('__version__ = "0.23beta"\n') + + self.file_russian = os.path.join(self.tmpdir, 'russian.py') + with open(self.file_russian, 'wb') as fp: + fp.write('# файл в русской кодировке\n\n'.encode('cp1251')) + fp.write('__version__ = "17.0"\n') + + def teardown_method(self, method): + shutil.rmtree(self.dist_dir) + + def test_get_version_files(self): + version = get_version(self.ver_python) + assert version == '0.23beta' + + version2 = get_version(self.ver_russian) + assert version2 == '17.0' + From 043573f058110c80d1ae0bd129a1efcc24953e17 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 10:55:00 +0300 Subject: [PATCH 4/8] Fix encoding and self version test failures https://travis-ci.org/pypa/setuptools/jobs/491170322#L747 --- setuptools/__init__.py | 2 +- setuptools/tests/test_get_version.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index d405ef1e5e..4723d9f7e5 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -240,7 +240,7 @@ def get_version(path, field='__version__'): # __version__ = "0.9" # current_version = 4.8.0 _, value = line.split(b'=') - version = value.strip(b' \'\"').decode() + version = value.strip(b' \n\t\'\"').decode() return version diff --git a/setuptools/tests/test_get_version.py b/setuptools/tests/test_get_version.py index ca48f6c80e..25bf056401 100644 --- a/setuptools/tests/test_get_version.py +++ b/setuptools/tests/test_get_version.py @@ -1,4 +1,4 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- """Tests for setuptools.get_version().""" import os @@ -12,7 +12,14 @@ def test_get_version(): version = get_version('setup.cfg', field='current_version') - assert version == __version__ + + # `setup.py egg_info` which is run in bootstrap.py during package + # installation adds `.post` prefix to setuptools.__version__ + # which becomes different from 'setup.cfg` file + + # https://setuptools.readthedocs.io/en/latest/setuptools.html#egg-info + + assert __version__.startswith(version + '.post') class TestFiles: From 4c964a4803fdc9e630adee86443ade1037d5498b Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 11:02:01 +0300 Subject: [PATCH 5/8] Rename get_version tests for clarity --- setuptools/tests/test_get_version.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_get_version.py b/setuptools/tests/test_get_version.py index 25bf056401..32b2391cae 100644 --- a/setuptools/tests/test_get_version.py +++ b/setuptools/tests/test_get_version.py @@ -10,7 +10,7 @@ from setuptools import __version__, get_version -def test_get_version(): +def test_own_version(): version = get_version('setup.cfg', field='current_version') # `setup.py egg_info` which is run in bootstrap.py during package @@ -38,10 +38,11 @@ def setup_method(self, method): def teardown_method(self, method): shutil.rmtree(self.dist_dir) - def test_get_version_files(self): + def test_python_file(self): version = get_version(self.ver_python) assert version == '0.23beta' + def test_non_utf8_python_file(self): version2 = get_version(self.ver_russian) assert version2 == '17.0' From d00fd5b76a5858acd186060f8fee5c652e51603b Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 11:11:35 +0300 Subject: [PATCH 6/8] Write file contents separately for each test --- setuptools/tests/test_get_version.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/setuptools/tests/test_get_version.py b/setuptools/tests/test_get_version.py index 32b2391cae..88ffda1526 100644 --- a/setuptools/tests/test_get_version.py +++ b/setuptools/tests/test_get_version.py @@ -26,23 +26,23 @@ class TestFiles: def setup_method(self, method): self.tmpdir = tempfile.mkdtemp() - self.file_python = os.path.join(self.tmpdir, 'version.py') - with open(self.file_python, 'w') as fp: - fp.write('__version__ = "0.23beta"\n') - - self.file_russian = os.path.join(self.tmpdir, 'russian.py') - with open(self.file_russian, 'wb') as fp: - fp.write('# файл в русской кодировке\n\n'.encode('cp1251')) - fp.write('__version__ = "17.0"\n') - def teardown_method(self, method): - shutil.rmtree(self.dist_dir) + shutil.rmtree(self.tmpdir) def test_python_file(self): - version = get_version(self.ver_python) + path = os.path.join(self.tmpdir, 'version.py') + with open(path, 'w') as fp: + fp.write('__version__ = "0.23beta"\n') + + version = get_version(path) assert version == '0.23beta' def test_non_utf8_python_file(self): - version2 = get_version(self.ver_russian) - assert version2 == '17.0' + path = os.path.join(self.tmpdir, 'russian.py') + with open(path, 'wb') as fp: + fp.write(u'# файл в русской кодировке\n\n'.encode('cp1251')) + fp.write('__version__ = "17.0"\n') + + version = get_version(path) + assert version == '17.0' From ded0994b4a904e5cdf89a5bfef4da9caaada76de Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 11:20:27 +0300 Subject: [PATCH 7/8] Fix for Python 3 and add missing shutil --- setuptools/tests/test_get_version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_get_version.py b/setuptools/tests/test_get_version.py index 88ffda1526..f9c93912f6 100644 --- a/setuptools/tests/test_get_version.py +++ b/setuptools/tests/test_get_version.py @@ -3,6 +3,7 @@ """Tests for setuptools.get_version().""" import os import codecs +import shutil import tempfile import pytest @@ -41,7 +42,7 @@ def test_non_utf8_python_file(self): path = os.path.join(self.tmpdir, 'russian.py') with open(path, 'wb') as fp: fp.write(u'# файл в русской кодировке\n\n'.encode('cp1251')) - fp.write('__version__ = "17.0"\n') + fp.write(u'__version__ = "17.0"\n'.encode('cp1251')) version = get_version(path) assert version == '17.0' From bd651a6c871259c1b25e28984d62b277e49e5e25 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 10 Feb 2019 11:26:41 +0300 Subject: [PATCH 8/8] Strip \r for Windows --- setuptools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 4723d9f7e5..632647c0b8 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -240,7 +240,7 @@ def get_version(path, field='__version__'): # __version__ = "0.9" # current_version = 4.8.0 _, value = line.split(b'=') - version = value.strip(b' \n\t\'\"').decode() + version = value.strip(b' \r\n\t\'\"').decode() return version