Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change development version number structure #271

Merged
merged 10 commits into from
Apr 8, 2022
9 changes: 6 additions & 3 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
import re
import sys
import types
from ast import parse
from inspect import getmembers, isfunction
from unittest.mock import MagicMock

from sphinx.ext.napoleon.docstring import GoogleDocstring

confpath = os.path.dirname(__file__)
sys.path.append(confpath)
rootpath = os.path.join(confpath, "..", "..")
sys.path.append(rootpath)

from docutil import insert_inheritance_diagram, package_classes

from scico._version import package_version


## See
## https://github.com/sphinx-doc/sphinx/issues/2115
Expand Down Expand Up @@ -162,8 +166,7 @@ def patched_parse(self):
# built documents.
#
# The short X.Y version.
with open(os.path.join(confpath, "..", "..", "scico", "__init__.py")) as f:
version = parse(next(filter(lambda line: line.startswith("__version__"), f))).body[0].value.s
version = package_version()
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
2 changes: 1 addition & 1 deletion scico/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
solving the inverse problems that arise in scientific imaging applications.
"""

__version__ = "0.0.3a1"
__version__ = "0.0.3.dev0"

import sys

Expand Down
102 changes: 102 additions & 0 deletions scico/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2020-2022 by SCICO Developers
# All rights reserved. BSD 3-clause License.
# This file is part of the SCICO package. Details of the copyright and
# user license can be found in the 'LICENSE' file distributed with the
# package.

"""Support functions for determining the package version."""


import os
import re
from ast import parse
from subprocess import PIPE, Popen
from typing import Any, Optional, Tuple, Union


def root_init_path() -> str: # pragma: no cover
"""Get the path to the package root `__init__.py` file.

Returns:
Path to the package root `__init__.py` file.
"""
return os.path.join(os.path.dirname(__file__), "__init__.py")


def variable_assign_value(path: str, var: str) -> Any:
"""Get variable initialization value from a Python file.

Args:
path: Path of Python file.
var: Name of variable.

Returns:
Value to which variable `var` is initialized.

Raises:
RuntimeError: If the statement initializing variable `var` is not
found.
"""
with open(path) as f:
try:
# See http://stackoverflow.com/questions/2058802
value = parse(next(filter(lambda line: line.startswith(var), f))).body[0].value.s
except StopIteration:
raise RuntimeError(f"Could not find initialization of variable {var}")
return value


def init_variable_assign_value(var: str) -> Any: # pragma: no cover
"""Get variable initialization value from package `__init__.py` file.

Args:
var: Name of variable.

Returns:
Value to which variable `var` is initialized.

Raises:
RuntimeError: If the statement initializing variable `var` is not
found.
"""
return variable_assign_value(root_init_path(), var)


def current_git_hash() -> Optional[str]: # nosec pragma: no cover
"""Get current short git hash.

Returns:
Short git hash of current commit, or ``None`` if no git repo found.
"""
process = Popen(["git", "rev-parse", "--short", "HEAD"], shell=False, stdout=PIPE, stderr=PIPE)
git_hash = process.communicate()[0].strip().decode("utf-8")
if git_hash == "":
git_hash = None
return git_hash


def package_version(split: bool = False) -> Union[str, Tuple[str, str]]: # pragma: no cover
"""Get current package version.

Args:
split: Flag indicating whether to return the package version as a
single string or split into a tuple of components.

Returns:
Package version string or tuple of strings.
"""
version = init_variable_assign_value("__version_")
if re.match(r"^[0-9\.]+$", version): # don't extend purely numeric version numbers
git_hash = None
else:
git_hash = current_git_hash()
if git_hash:
git_hash = "+" + git_hash
else:
git_hash = ""
if split:
version = (version, git_hash)
else:
version = version + git_hash
return version
6 changes: 3 additions & 3 deletions scico/optimize/admm.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,11 @@ def objective(
f(\mb{x}) + \sum_{i=1}^N g_i(\mb{z}_i) \;.

Args:
x: Point at which to evaluate objective function. If `None`,
x: Point at which to evaluate objective function. If ``None``,
the objective is evaluated at the current iterate
:code:`self.x`.
z_list: Point at which to evaluate objective function. If
`None`, the objective is evaluated at the current iterate
``None``, the objective is evaluated at the current iterate
:code:`self.z_list`.

Returns:
Expand Down Expand Up @@ -549,7 +549,7 @@ def norm_primal_residual(self, x: Optional[Union[JaxArray, BlockArray]] = None)

Args:
x: Point at which to evaluate primal residual.
If `None`, the primal residual is evaluated at the
If ``None``, the primal residual is evaluated at the
current iterate :code:`self.x`.

Returns:
Expand Down
8 changes: 8 additions & 0 deletions scico/test/test_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from scico._version import variable_assign_value

test_var = 12345


def test_var_val():
var_val = variable_assign_value(__file__, "test_var")
assert var_val == test_var
33 changes: 12 additions & 21 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,24 @@

"""SCICO package configuration."""

import importlib.util
import os
import os.path
import re
import site
from ast import parse
import sys

from setuptools import find_packages, setup

name = "scico"


def get_init_variable_value(var):
"""
Get version number from scico/__init__.py
See http://stackoverflow.com/questions/2058802
"""

with open(os.path.join(name, "__init__.py")) as f:
try:
value = parse(next(filter(lambda line: line.startswith(var), f))).body[0].value.s
except StopIteration:
raise RuntimeError(f"Could not find initialization of variable {var}")
return value


version = get_init_variable_value("__version_")
# Import module scico._version without executing __init__.py
spec = importlib.util.spec_from_file_location("_version", os.path.join("scico", "_version.py"))
module = importlib.util.module_from_spec(spec)
sys.modules["_version"] = module
spec.loader.exec_module(module)
from _version import init_variable_assign_value, package_version

name = "scico"
version = package_version()
packages = find_packages()

longdesc = """
Expand All @@ -42,7 +33,7 @@ def get_init_variable_value(var):
install_requires = [line.strip() for line in lines]

# Check that jaxlib version requirements in __init__.py and requirements.txt match
jaxlib_ver = get_init_variable_value("jaxlib_ver_req")
jaxlib_ver = init_variable_assign_value("jaxlib_ver_req")
jaxlib_req_str = list(filter(lambda s: s.startswith("jaxlib"), install_requires))[0]
m = re.match("jaxlib[=<>]+([\d\.]+)", jaxlib_req_str)
if not m:
Expand All @@ -55,7 +46,7 @@ def get_init_variable_value(var):
)

# Check that jax version requirements in __init__.py and requirements.txt match
jax_ver = get_init_variable_value("jax_ver_req")
jax_ver = init_variable_assign_value("jax_ver_req")
jax_req_str = list(
filter(lambda s: s.startswith("jax") and not s.startswith("jaxlib"), install_requires)
)[0]
Expand Down