Skip to content

Commit

Permalink
Merge pull request #44 from jbweston/feat/alternate-package-layout
Browse files Browse the repository at this point in the history
Allow alternate package layouts

Closes #38
Closes #39
  • Loading branch information
jbweston authored Oct 14, 2021
2 parents 5dbd37c + 6643f29 commit e93faaf
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 107 deletions.
51 changes: 28 additions & 23 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,37 @@ jobs:
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Set up Git
- name: Set up Git and Pip
run: |
git config --global user.email "[email protected]"
git config --global user.name "Your Name"
python -m pip install --upgrade pip
python -m pip install wheel
git --version
python --version
- name: Set up dummy Python project with miniver
- name: Install miniver
run: |
cd ..
git init my_package
cd my_package
python ../miniver/ci/create_package.py
git add .
git commit -m "Initialization of package"
git tag -a 0.0.0 -m "First version for miniver"
- name: Install dummy Python project with miniver
run: |
cd ../my_package
pip install -e .
- name: Test versioning of dummy project
pip install .
- name: Set up minimal python packages
run: |
python -c "import my_package; assert my_package.__version__ == '0.0.0'"
cd ../my_package
echo "# Extra comment" >> setup.py
python -c "import my_package; assert my_package.__version__.endswith('dirty')"
python -c "import my_package; assert my_package.__version__.startswith('0.0.0')"
git commit -a -m "new comment"
python -c "import my_package; assert my_package.__version__.startswith('0.0.0.dev1')"
git tag -a 0.0.1 -m "0.0.1"
python -c "import my_package; assert my_package.__version__ == '0.0.1'"
cd ..
# simple package
python miniver/ci/create_package.py simple-distr simple_pkg
# simple package in 'src' layout
python miniver/ci/create_package.py simple-src-distr simple_src_pkg --src-layout
# namespace package
python miniver/ci/create_package.py ns-distr nspkg.simple_pkg
# namespace package in 'src' layout
python miniver/ci/create_package.py ns-src-distr nspkg.simple_src_pkg --src-layout
- name: Test versioning of simple package
shell: bash
run: cd .. && miniver/ci/test_package.sh simple-distr simple_pkg
- name: Test versioning of simple src-layout package
shell: bash
run: cd .. && miniver/ci/test_package.sh simple-src-distr simple_src_pkg
- name: Test versioning of namespace package
shell: bash
run: cd .. && miniver/ci/test_package.sh ns-distr nspkg.simple_pkg
- name: Test versioning of namespace src-layout package
shell: bash
run: cd .. && miniver/ci/test_package.sh ns-src-distr nspkg.simple_src_pkg
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add a 'ver' command that prints the detected version
- Running 'miniver' without any arguments invokes the 'ver' command
- Miniver now works with namespace packages

## [0.7.0] - 2020-08-15
### Added
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ works with Python 2](https://github.com/cmarquardt/miniver2)
## Usage
The simplest way to use Miniver is to run the following in your project root:
```
curl https://raw.githubusercontent.com/jbweston/miniver/master/bin/miniver | python - install <your_package_directory>
curl https://raw.githubusercontent.com/jbweston/miniver/master/miniver/app.py | python - install <your_package_directory>
```
This will grab the latest files from GitHub and set up Miniver for your project.

Expand Down Expand Up @@ -69,15 +69,18 @@ del _version
```python
# Your project's setup.py

from setuptools import setup

# Loads _version.py module without importing the whole package.
def get_version_and_cmdclass(package_name):
def get_version_and_cmdclass(pkg_path):
import os
from importlib.util import module_from_spec, spec_from_file_location
spec = spec_from_file_location('version',
os.path.join(package_name, '_version.py'))
spec = spec_from_file_location(
'version', os.path.join(pkg_path, '_version.py'),
)
module = module_from_spec(spec)
spec.loader.exec_module(module)
return module.__version__, module.get_cmdclass()
return module.__version__, module.get_cmdclass(pkg_path)


version, cmdclass = get_version_and_cmdclass('my_package')
Expand Down
130 changes: 95 additions & 35 deletions ci/create_package.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,95 @@
from pathlib import Path
from shutil import copyfile

Path("my_package").mkdir(exist_ok=True)
copyfile("../miniver/miniver/_static_version.py", "my_package/_static_version.py")
copyfile("../miniver/miniver/_version.py", "my_package/_version.py")

README_filename = "../miniver/README.md"


def write_snippet_from_readme(outfile, start_marker, file_header=None):
# Create the setup file
with open(README_filename) as f:
for line in f:
if line.startswith(start_marker):
break
else:
raise RuntimeError(
"Could not find start_marker: {}" "".format(start_marker)
)
with open(outfile, "w") as out:
out.write(line)
if file_header is not None:
out.write(file_header)
for line in f:
if line.startswith("```"):
break
out.write(line)


write_snippet_from_readme(
"setup.py", "# Your project's setup.py", "from setuptools import setup\n"
)
write_snippet_from_readme(".gitattributes", "# Your project's .gitattributes")
write_snippet_from_readme("my_package/__init__.py", "# Your package's __init__.py")
import argparse
from contextlib import contextmanager
from functools import partial
import os.path
from os import chdir, makedirs
from shutil import rmtree
from subprocess import run, PIPE, CalledProcessError
from sys import exit, stderr
from textwrap import indent


@contextmanager
def log(msg, where=stderr):
pp = partial(print, file=where)
pp("{}...".format(msg), end="")
try:
yield
except KeyboardInterrupt:
pp("INTERRUPTED")
exit(2)
except CalledProcessError as e:
pp("FAILED")
pp("Subprocess '{}' failed with exit code {}".format(e.cmd, e.returncode))
if e.stdout:
pp("---- stdout ----")
pp(e.stdout.decode())
pp("----------------")
if e.stderr:
pp("---- stderr ----")
pp(e.stderr.decode())
pp("----------------")
exit(e.returncode)
except Exception as e:
print("FAILED", file=where)
print(str(e), file=where)
exit(1)
else:
print("OK", file=where)


def main():
parser = argparse.ArgumentParser()
parser.add_argument("distribution", help="Distribution package name")
parser.add_argument("package", help="Dotted package name")
parser.add_argument("--src-layout", action="store_true")
args = parser.parse_args()

distr, pkg, src_pkg = (getattr(args, x) for x in ("distribution", "package", "src_layout"))

path = os.path.join("src" if src_pkg else "", pkg.replace(".", os.path.sep))

with log("Ensuring '{}' is removed".format(distr)):
rmtree(args.distribution, ignore_errors=True)

with log("Initializing git repository in '{}'".format(distr)):
run("git init {}".format(distr), shell=True, check=True, stdout=PIPE, stderr=PIPE)
chdir(distr)
makedirs(path)

with log("Installing miniver in '{}'".format(os.path.join(distr, path))):
r = run(
"miniver install {}".format(path),
shell=True,
check=True,
stdout=PIPE,
stderr=PIPE,
)
setup_template = r.stdout.decode("utf8")

with log("Writing gitignore"):
with open(".gitignore", "w") as f:
f.write("\n".join([
"dist",
"build",
"__pycache__",
"*.egg-info",
]))

with log("Writing setup.py"):
lines = [
"name='{}',".format(distr),
"packages=['{}'],".format(pkg),
]
if src_pkg:
lines.append("package_dir={'': 'src'},")
replacement = indent("\n".join(lines), " ")

with open("setup.py", "w") as f:
# This is tightly coupled to the setup.py template: there is a
# call to 'setup()' with an ellipsis on a single line.
f.write(setup_template.replace(" ...,", replacement))


if __name__ == "__main__":
main()
82 changes: 82 additions & 0 deletions ci/test_package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
set -e

distr=$1
pkg=$2

function test_version() {
echo "Testing: $pkg.__version__$1"
python -c "import $pkg; assert $pkg.__version__$1"
}

# "./" to ensure we don't pull from PyPI
pip install -e ./$distr

test_version "== 'unknown'"

pushd $distr
git add .
git commit -m "First commit"
git tag -a 0.0.0 -m "0.0.0"
echo "Tagged 0.0.0"
popd

test_version "== '0.0.0'"

pushd $distr
echo "# Extra comment" >> setup.py
echo "Modified working directory"
popd

test_version ".startswith('0.0.0')"
test_version ".endswith('dirty')"

pushd $distr
git commit -a -m "new comment"
echo "Committed changes"
popd

test_version ".startswith('0.0.0.dev1')"

pushd $distr
git tag -a 0.0.1 -m "0.0.1"
echo "Tagged 0.0.1"
popd

test_version "== '0.0.1'"

# Now test against "real" (non-editable) installations
pip uninstall -y $distr

pushd $distr
git commit --allow-empty -m 'next commit'
git tag -a 0.0.2 -m "0.0.2"
echo "Tagged 0.0.2"
popd

# First a source distribution

echo "Testing setup.py sdist"
pushd $distr
python setup.py sdist
pip install dist/*.tar.gz
popd

test_version "== '0.0.2'"

pip uninstall -y $distr

pushd $distr
git commit --allow-empty -m 'final commit'
git tag -a 0.0.3 -m "0.0.3"
echo "Tagged 0.0.3"
popd

# Then a wheel distribution

echo "Testing setup.py bdist_wheel"
pushd $distr
python setup.py bdist_wheel
pip install dist/*.whl
popd

test_version "== '0.0.3'"
Loading

0 comments on commit e93faaf

Please sign in to comment.