From 656e185c6a6caaa72b20e164141f2a7577b0e7eb Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Sun, 11 Nov 2018 19:24:37 +0300 Subject: [PATCH 1/2] Simplify version parsing example This removes regexp and adds `import os`. File is read in `cp437` because this encoding has no missing code points while converting to `utf-8` string. Only ASCII set is used anyways. --- .../single-sourcing-package-version.rst | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/source/guides/single-sourcing-package-version.rst b/source/guides/single-sourcing-package-version.rst index 19e9a21e7..d141b4d01 100644 --- a/source/guides/single-sourcing-package-version.rst +++ b/source/guides/single-sourcing-package-version.rst @@ -8,34 +8,28 @@ Single-sourcing the package version There are many techniques to maintain a single source of truth for the version number of your project: -#. Read the file in :file:`setup.py` and parse the version with a regex. - Example ( from `pip setup.py - `_):: - - here = os.path.abspath(os.path.dirname(__file__)) - - def read(*parts): - with codecs.open(os.path.join(here, *parts), 'r') as fp: - return fp.read() - - def find_version(*file_paths): - version_file = read(*file_paths) - version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", - version_file, re.M) - if version_match: - return version_match.group(1) - raise RuntimeError("Unable to find version string.") +#. Read the file with version info in :file:`setup.py` and parse version line. + Example:: + + import os + + def get_version(rel_path): + current_dir = os.path.abspath(os.path.dirname(__file__)) + version_file = os.path.join(current_dir, rel_path) + for line in open(version_file, 'rb'): + # Decode to unicode for PY2/PY3 in a fail-safe way + line = line.decode('cp437') + if line.startswith('__version__'): + # __version__ = "0.9" + delim = '\"' if '\"' in line else '\'' + return line.split(delim)[1] setup( ... - version=find_version("package", "__init__.py") + version=get_version("package/__init__.py"), ... ) - .. note:: - - This technique has the disadvantage of having to deal with complexities of regular expressions. - #. Use an external build tool that either manages updating both locations, or offers an API that both locations can use. From 7d7a1713cebfe5c2d9c162ff80678f9c4a6e863d Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Mon, 26 Nov 2018 10:06:48 +0300 Subject: [PATCH 2/2] Parse __version__ file as binary To avoid problems with unknown encoding, file with __version__ is processed as binary and the final number is converted to string just before the function returns. --- source/guides/single-sourcing-package-version.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/guides/single-sourcing-package-version.rst b/source/guides/single-sourcing-package-version.rst index d141b4d01..f597d2a62 100644 --- a/source/guides/single-sourcing-package-version.rst +++ b/source/guides/single-sourcing-package-version.rst @@ -17,12 +17,10 @@ number of your project: current_dir = os.path.abspath(os.path.dirname(__file__)) version_file = os.path.join(current_dir, rel_path) for line in open(version_file, 'rb'): - # Decode to unicode for PY2/PY3 in a fail-safe way - line = line.decode('cp437') - if line.startswith('__version__'): + if line.startswith(b'__version__'): # __version__ = "0.9" - delim = '\"' if '\"' in line else '\'' - return line.split(delim)[1] + delim = b'\"' if b'\"' in line else b'\'' + return line.split(delim)[1].decode() setup( ...