Skip to content

Commit

Permalink
Migrate to setup.cfg
Browse files Browse the repository at this point in the history
  • Loading branch information
waveform80 committed Mar 17, 2021
1 parent 6532c1f commit b167bef
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 314 deletions.
4 changes: 0 additions & 4 deletions MANIFEST.in

This file was deleted.

88 changes: 12 additions & 76 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,18 @@
PYTHON=python
PIP=pip
PYTEST=py.test
COVERAGE=coverage
TWINE=twine
PYFLAGS=
DEST_DIR=/

# Horrid hack to ensure setuptools is installed in our python environment. This
# is necessary with Python 3.3's venvs which don't install it by default.
ifeq ($(shell python -c "import setuptools" 2>&1),)
SETUPTOOLS:=
else
SETUPTOOLS:=$(shell wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | $(PYTHON))
endif

# Calculate the base names of the distribution, the location of all source,
# documentation, packaging, icon, and executable script files
NAME:=$(shell $(PYTHON) $(PYFLAGS) setup.py --name)
VER:=$(shell $(PYTHON) $(PYFLAGS) setup.py --version)
ifeq ($(shell lsb_release -si),Ubuntu)
DEB_SUFFIX:=ubuntu1
else
DEB_SUFFIX:=
endif
DEB_ARCH:=$(shell dpkg --print-architecture)
PYVER:=$(shell $(PYTHON) $(PYFLAGS) -c "import sys; print('py%d.%d' % sys.version_info[:2])")
PY_SOURCES:=$(shell \
$(PYTHON) $(PYFLAGS) setup.py egg_info >/dev/null 2>&1 && \
grep -v "\.egg-info" $(NAME).egg-info/SOURCES.txt)
DEB_SOURCES:=debian/changelog \
debian/control \
debian/copyright \
debian/rules \
debian/docs \
$(wildcard debian/*.init) \
$(wildcard debian/*.default) \
$(wildcard debian/*.manpages) \
$(wildcard debian/*.docs) \
$(wildcard debian/*.doc-base) \
$(wildcard debian/*.desktop)
DOC_SOURCES:=docs/conf.py \
$(wildcard docs/*.png) \
$(wildcard docs/*.svg) \
Expand All @@ -56,17 +30,6 @@ SUBDIRS:=
DIST_WHEEL=dist/$(NAME)-$(VER)-py2.py3-none-any.whl
DIST_TAR=dist/$(NAME)-$(VER).tar.gz
DIST_ZIP=dist/$(NAME)-$(VER).zip
DIST_DEB=dist/python-$(NAME)_$(VER)$(DEB_SUFFIX)_all.deb \
dist/python3-$(NAME)_$(VER)$(DEB_SUFFIX)_all.deb \
dist/python-$(NAME)-doc_$(VER)$(DEB_SUFFIX)_all.deb \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_$(DEB_ARCH).build \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_$(DEB_ARCH).buildinfo \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_$(DEB_ARCH).changes
DIST_DSC=dist/$(NAME)_$(VER)$(DEB_SUFFIX).tar.xz \
dist/$(NAME)_$(VER)$(DEB_SUFFIX).dsc \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_source.build \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_source.buildinfo \
dist/$(NAME)_$(VER)$(DEB_SUFFIX)_source.changes


# Default target
Expand All @@ -76,10 +39,9 @@ all:
@echo "make test - Run tests"
@echo "make doc - Generate HTML and PDF documentation"
@echo "make source - Create source package"
@echo "make egg - Generate a PyPI egg package"
@echo "make wheel - Generate a PyPI wheel package"
@echo "make zip - Generate a source zip package"
@echo "make tar - Generate a source tar package"
@echo "make deb - Generate Debian packages"
@echo "make dist - Generate all packages"
@echo "make clean - Get rid of all generated files"
@echo "make release - Create and tag a new release"
Expand All @@ -102,9 +64,7 @@ zip: $(DIST_ZIP)

tar: $(DIST_TAR)

deb: $(DIST_DEB) $(DIST_DSC)

dist: $(DIST_WHEEL) $(DIST_DEB) $(DIST_DSC) $(DIST_TAR) $(DIST_ZIP)
dist: $(DIST_WHEEL) $(DIST_TAR) $(DIST_ZIP)

develop:
@# These have to be done separately to avoid a cockup...
Expand All @@ -113,18 +73,17 @@ develop:
$(PIP) install -e .[doc,test]

test:
$(COVERAGE) run --rcfile coverage.cfg -m $(PYTEST) tests -v -r sx
$(COVERAGE) report --rcfile coverage.cfg
$(PYTEST) tests

clean:
dh_clean
rm -fr dist/ $(NAME).egg-info/ tags
rm -fr dist/ build/ .pytest_cache/ .mypy_cache/ $(NAME).egg-info/ tags .coverage
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir clean; \
done
find $(CURDIR) -name "*.pyc" -delete

tags: $(PY_SOURCES)
ctags -R --exclude="build/*" --exclude="debian/*" --exclude="docs/*" --languages="Python"
ctags -R --exclude="build/*" --exclude="docs/*" --languages="Python"

$(SUBDIRS):
$(MAKE) -C $@
Expand All @@ -138,37 +97,14 @@ $(DIST_ZIP): $(PY_SOURCES) $(SUBDIRS)
$(DIST_WHEEL): $(PY_SOURCES) $(SUBDIRS)
$(PYTHON) $(PYFLAGS) setup.py bdist_wheel --universal

$(DIST_DEB): $(PY_SOURCES) $(SUBDIRS) $(DEB_SOURCES) $(DIST_TAR)
cp $(DIST_TAR) ../$(NAME)_$(VER).orig.tar.gz
debuild -b
mkdir -p dist/
for f in $(DIST_DEB); do cp ../$${f##*/} dist/; done

$(DIST_DSC): $(PY_SOURCES) $(SUBDIRS) $(DEB_SOURCES) $(DIST_TAR)
cp $(DIST_TAR) ../$(NAME)_$(VER).orig.tar.gz
debuild -S
mkdir -p dist/
for f in $(DIST_DSC); do cp ../$${f##*/} dist/; done

copyrights: $(PY_SOURCES) $(DOC_SOURCES)
./copyrights

changelog: $(PY_SOURCES) $(DOC_SOURCES) $(DEB_SOURCES)
release:
$(MAKE) clean
# ensure there are no current uncommitted changes
test -z "$(shell git status --porcelain)"
# update the debian changelog with new release information
dch --newversion $(VER)$(DEB_SUFFIX)
# commit the changes and add a new tag
git commit debian/changelog -m "Updated changelog for release $(VER)"

release: $(DIST_DEB) $(DIST_DSC) $(DIST_TAR) $(DIST_WHEEL)
git tag -s v$(VER) -m "Release v$(VER)"
git push --tags
# build a source archive and upload to PyPI
git push origin v$(VER)

upload: $(DIST_TAR) $(DIST_WHEEL)
$(TWINE) check $(DIST_TAR) $(DIST_WHEEL)
$(TWINE) upload $(DIST_TAR) $(DIST_WHEEL)
# build the deb source archive and upload to Raspbian
dput raspberrypi dist/$(NAME)_$(VER)$(DEB_SUFFIX)_source.changes
dput raspberrypi dist/$(NAME)_$(VER)$(DEB_SUFFIX)_$(DEB_ARCH).changes

.PHONY: all install develop test doc source egg wheel zip tar deb dist clean tags release upload $(SUBDIRS)
.PHONY: all install develop test doc source wheel zip tar dist clean tags release upload $(SUBDIRS)
56 changes: 33 additions & 23 deletions copyrights
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ derives the authorship and copyright years information from the git history
of the project; hence, this script must be run within a git repository.
"""

from __future__ import annotations

import os
import sys
assert sys.version_info >= (3, 6), 'Script requires Python 3.6+'
import tempfile
import typing as t
from argparse import ArgumentParser, Namespace
from configparser import ConfigParser
from operator import attrgetter
Expand All @@ -18,14 +21,13 @@ from datetime import datetime
from subprocess import Popen, PIPE, DEVNULL
from pathlib import Path
from fnmatch import fnmatch
from typing import NamedTuple, Iterator, List, Tuple, Set, Union, Optional


SPDX_PREFIX = 'SPDX-License-Identifier:'
COPYRIGHT_PREFIX = 'Copyright (c)'


def main(args: List[str] = None):
def main(args: t.List[str] = None):
if args is None:
args = sys.argv[1:]
config = get_config(args)
Expand All @@ -41,7 +43,7 @@ def main(args: List[str] = None):
target.write(chunk)


def get_config(args: List[str]) -> Namespace:
def get_config(args: t.List[str]) -> Namespace:
config = ConfigParser(
defaults={
'include': '**/*',
Expand All @@ -52,10 +54,10 @@ def get_config(args: List[str]) -> Namespace:
'spdx_prefix': SPDX_PREFIX,
'copy_prefix': COPYRIGHT_PREFIX,
},
delimiters=('=',), default_section='settings',
delimiters=('=',), default_section='copyrights:settings',
empty_lines_in_values=False, interpolation=None,
converters={'list': lambda s: s.strip().splitlines() })
config.read('copyrights.cfg')
config.read('setup.cfg')
sect = config[config.default_section]

parser = ArgumentParser(description=__doc__)
Expand Down Expand Up @@ -113,10 +115,10 @@ def get_config(args: List[str]) -> Namespace:
return ns


class Copyright(NamedTuple):
class Copyright(t.NamedTuple):
author: str
email: str
years: Set[int]
years: t.Set[int]

def __str__(self):
if len(self.years) > 1:
Expand All @@ -126,8 +128,8 @@ class Copyright(NamedTuple):
return f'{years} {self.author} <{self.email}>'


def get_copyrights(include: Set[str], exclude: Set[str])\
-> Iterator[Tuple[Path, List[Copyright]]]:
def get_copyrights(include: t.Set[str], exclude: t.Set[str])\
-> t.Iterator[t.Tuple[Path, t.Container[Copyright]]]:
sorted_blame = sorted(
get_contributions(include, exclude),
key=lambda c: (c.path, c.author, c.email)
Expand All @@ -147,15 +149,15 @@ def get_copyrights(include: Set[str], exclude: Set[str])\
yield path, copyrights


class Contribution(NamedTuple):
class Contribution(t.NamedTuple):
author: str
email: str
year: int
path: Path


def get_contributions(include: Set[str], exclude: Set[str])\
-> Iterator[Contribution]:
def get_contributions(include: t.Set[str], exclude: t.Set[str])\
-> t.Iterator[Contribution]:
for path in get_source_paths(include, exclude):
blame = Popen(
['git', 'blame', '--line-porcelain', 'HEAD', '--', str(path)],
Expand Down Expand Up @@ -186,7 +188,8 @@ def get_contributions(include: Set[str], exclude: Set[str])\
assert blame.returncode == 0


def get_source_paths(include: Set[str], exclude: Set[str]) -> Iterator[Path]:
def get_source_paths(include: t.Set[str], exclude: t.Set[str])\
-> t.Iterator[Path]:
ls_tree = Popen(
['git', 'ls-tree', '-r', '--name-only', 'HEAD'],
stdout=PIPE, stderr=DEVNULL, universal_newlines=True)
Expand All @@ -203,9 +206,9 @@ def get_source_paths(include: Set[str], exclude: Set[str]) -> Iterator[Path]:
assert ls_tree.returncode == 0


class License(NamedTuple):
ident: Optional[str]
text: List[str]
class License(t.NamedTuple):
ident: t.Optional[str]
text: t.List[str]


def get_license(path: Path, *, spdx_prefix: str = SPDX_PREFIX) -> License:
Expand Down Expand Up @@ -253,20 +256,26 @@ class CopyWriter:
'.sql': '--',
}

def __init__(self, license='LICENSE.txt', preamble=(),
spdx_prefix=SPDX_PREFIX, copy_prefix=COPYRIGHT_PREFIX):
def __init__(self, license: Path=Path('LICENSE.txt'),
preamble: t.List[str]=None,
spdx_prefix: str=SPDX_PREFIX,
copy_prefix: str=COPYRIGHT_PREFIX):
if preamble is None:
preamble = []
self.license = get_license(license, spdx_prefix=spdx_prefix)
self.preamble = preamble
self.spdx_prefix = spdx_prefix
self.copy_prefix = copy_prefix

@classmethod
def from_config(cls, config):
def from_config(cls, config: Namespace) -> CopyWriter:
return cls(
config.license, config.preamble,
config.spdx_prefix, config.copy_prefix)

def transform(self, source, copyrights, *, comment_prefix=None):
def transform(self, source: t.TextIO,
copyrights: t.List[Copyright], *,
comment_prefix: str=None) -> t.Iterator[str]:
if comment_prefix is None:
comment_prefix = self.COMMENTS[Path(source.name).suffix]
license_start = self.license.text[0]
Expand All @@ -279,7 +288,7 @@ class CopyWriter:
yield line
empty = False
elif linenum < 3 and (
'set fileencoding=' in line or '-*- coding:' in line):
'fileencoding=' in line or '-*- coding:' in line):
yield line
empty = False
elif line.rstrip() == comment_prefix:
Expand Down Expand Up @@ -313,7 +322,8 @@ class CopyWriter:
elif state == 'body':
yield line

def _generate_header(self, copyrights, comment_prefix, empty):
def _generate_header(self, copyrights: t.Iterable[Copyright],
comment_prefix: str, empty: bool) -> t.Iterator[str]:
if not empty:
yield comment_prefix + '\n'
for line in self.preamble:
Expand Down Expand Up @@ -356,7 +366,7 @@ class AtomicReplaceFile:
If ``None`` (the default), the temporary file will be opened in binary
mode. Otherwise, this specifies the encoding to use with text mode.
"""
def __init__(self, path: Union[str, Path], encoding: str = None):
def __init__(self, path: t.Union[str, Path], encoding: str = None):
if isinstance(path, str):
path = Path(path)
self._path = path
Expand Down
11 changes: 0 additions & 11 deletions copyrights.cfg

This file was deleted.

18 changes: 0 additions & 18 deletions coverage.cfg

This file was deleted.

0 comments on commit b167bef

Please sign in to comment.