diff --git a/.gitignore b/.gitignore index 88c630b..b7b6c67 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ local_settings.py keeper build/* +ChangeLog build-stamp python_melangeclient.egg-info .tox diff --git a/MANIFEST.in b/MANIFEST.in index df9dd8d..330de3f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,5 @@ +include ChangeLog +include openstack-common.conf include README.rst +include tox.ini include melange/client/views/*.tpl diff --git a/melange/client/__init__.py b/melange/client/__init__.py index 9ab7051..e69de29 100644 --- a/melange/client/__init__.py +++ b/melange/client/__init__.py @@ -1,30 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import gettext - -from melange.client.client import HTTPClient -from melange.client.client import AuthorizationClient - - -# NOTE(jkoelker) should this be melange.client? Are translations going -# to be separate? -gettext.install('melange', unicode=1) - - -__all__ = [HTTPClient, - AuthorizationClient] diff --git a/melange/client/openstack/__init__.py b/melange/client/openstack/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/melange/client/openstack/common/__init__.py b/melange/client/openstack/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/melange/client/openstack/common/setup.py b/melange/client/openstack/common/setup.py new file mode 100644 index 0000000..4f82281 --- /dev/null +++ b/melange/client/openstack/common/setup.py @@ -0,0 +1,121 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Utilities with minimum-depends for use in setup.py +""" + +import os +import re +import subprocess + + +def parse_mailmap(mailmap='.mailmap'): + mapping = {} + if os.path.exists(mailmap): + fp = open(mailmap, 'r') + for l in fp: + l = l.strip() + if not l.startswith('#') and ' ' in l: + canonical_email, alias = l.split(' ') + mapping[alias] = canonical_email + return mapping + + +def str_dict_replace(s, mapping): + for s1, s2 in mapping.iteritems(): + s = s.replace(s1, s2) + return s + + +# Get requirements from the first file that exists +def get_reqs_from_files(requirements_files): + reqs_in = [] + for requirements_file in requirements_files: + if os.path.exists(requirements_file): + return open(requirements_file, 'r').read().split('\n') + return [] + + +def parse_requirements(requirements_files=['requirements.txt', + 'tools/pip-requires']): + requirements = [] + for line in get_reqs_from_files(requirements_files): + if re.match(r'\s*-e\s+', line): + requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', + line)) + elif re.match(r'\s*-f\s+', line): + pass + else: + requirements.append(line) + + return requirements + + +def parse_dependency_links(requirements_files=['requirements.txt', + 'tools/pip-requires']): + dependency_links = [] + for line in get_reqs_from_files(requirements_files): + if re.match(r'(\s*#)|(\s*$)', line): + continue + if re.match(r'\s*-[ef]\s+', line): + dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) + return dependency_links + + +def write_requirements(): + venv = os.environ.get('VIRTUAL_ENV', None) + if venv is not None: + with open("requirements.txt", "w") as req_file: + output = subprocess.Popen(["pip", "-E", venv, "freeze", "-l"], + stdout=subprocess.PIPE) + requirements = output.communicate()[0].strip() + req_file.write(requirements) + + +def run_git_command(cmd): + output = subprocess.Popen(["/bin/sh", "-c", cmd], + stdout=subprocess.PIPE) + return output.communicate()[0].strip() + + +def write_vcsversion(location): + if os.path.isdir('.git'): + branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "' + branch_nick = run_git_command(branch_nick_cmd) + revid_cmd = "git --no-pager log --max-count=1 --pretty=oneline" + revid = run_git_command(revid_cmd).split()[0] + revno_cmd = "git --no-pager log --oneline | wc -l" + revno = run_git_command(revno_cmd) + with open(location, 'w') as version_file: + version_file.write(""" +# This file is automatically generated by setup.py, So don't edit it. :) +version_info = { + 'branch_nick': '%s', + 'revision_id': '%s', + 'revno': %s +} +""" % (branch_nick, revid, revno)) + + +def write_git_changelog(): + if os.path.isdir('.git'): + git_log_gnu = 'git log --format="%ai %aN %n%n%x09* %s%d%n"' + changelog = run_git_command(git_log_gnu) + mailmap = parse_mailmap() + with open("ChangeLog", "w") as changelog_file: + changelog_file.write(str_dict_replace(changelog, mailmap)) diff --git a/openstack-common.conf b/openstack-common.conf new file mode 100644 index 0000000..d70827e --- /dev/null +++ b/openstack-common.conf @@ -0,0 +1,7 @@ +[DEFAULT] + +# The list of modules to copy from openstack-common +modules=setup + +# The base module to hold the copy of openstack.common +base=melange.client diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..c60facc --- /dev/null +++ b/pylintrc @@ -0,0 +1,38 @@ +# The format of this file isn't really documented; just use --generate-rcfile + +[Messages Control] +# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future +# C0111: Don't require docstrings on every method +# W0511: TODOs in code comments are fine. +# W0142: *args and **kwargs are fine. +# W0622: Redefining id is fine. +disable=C0111,W0511,W0142,W0622 + +[Basic] +# Variable names can be 1 to 31 characters long, with lowercase and underscores +variable-rgx=[a-z_][a-z0-9_]{0,30}$ + +# Argument names can be 2 to 31 characters long, with lowercase and underscores +argument-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Method names should be at least 3 characters long +# and be lowecased with underscores +method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ + +# Module names matching melange-* are ok (files in bin/) +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(melange-[a-z0-9_-]+))$ + +# Don't require docstrings on tests. +no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ + +[Design] +max-public-methods=100 +min-public-methods=0 +max-args=6 + +[Variables] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +# _ is used by our localization +additional-builtins=_ diff --git a/setup.cfg b/setup.cfg index 8bbf905..917219d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,13 +1,6 @@ [nosetests] verbosity=2 detailed-errors=1 -with-tissue=1 -tissue-repeat=1 -tissue-show-pep8=1 -tissue-show-source=1 -tissue-inclusive=1 -tissue-color=1 -tissue-package=melange.client with-openstack=1 openstack-red=0.1 openstack-yellow=0.075 diff --git a/setup.py b/setup.py index 2b4ea14..e506fb4 100644 --- a/setup.py +++ b/setup.py @@ -15,10 +15,23 @@ import os import sys +from melange.client.openstack.common.setup import parse_requirements +from melange.client.openstack.common.setup import parse_dependency_links +from melange.client.openstack.common.setup import write_requirements +from melange.client.openstack.common.setup import write_git_changelog + +from setuptools.command.sdist import sdist import setuptools version = "0.1" -install_requires = ["httplib2", "pyyaml"] + + +class local_sdist(sdist): + """Customized sdist hook - builds the ChangeLog file from VC first""" + def run(self): + write_git_changelog() + sdist.run(self) +cmdclass = {'sdist': local_sdist} if sys.version_info < (2, 6): install_requires.append("simplejson") @@ -34,6 +47,8 @@ classifiers = ["Development Status :: 5 - Production/Stable", console_scripts = ["melange = melange.client.cli:main"] +write_requirements() + def read_file(file_name): return open(os.path.join(os.path.dirname(__file__), @@ -46,12 +61,14 @@ setuptools.setup(name="python-melangeclient", long_description=read_file("README.rst"), license="Apache License, Version 2.0", url="https://github.com/openstack/python-melangeclient", + cmdclass=cmdclass, classifiers=classifiers, author="Openstack Melange Team", author_email="openstack@lists.launchpad.net", include_package_data=True, packages=setuptools.find_packages(exclude=["tests"]), - install_requires=install_requires, - entry_points = {"console_scripts": console_scripts}, + install_requires=parse_requirements(), + dependency_links=parse_dependency_links(), + entry_points={"console_scripts": console_scripts}, zip_safe=False, ) diff --git a/tools/pip-requires b/tools/pip-requires new file mode 100644 index 0000000..7096c32 --- /dev/null +++ b/tools/pip-requires @@ -0,0 +1,2 @@ +pyyaml +httplib2 diff --git a/tools/test-requires b/tools/test-requires new file mode 100644 index 0000000..4de4b4a --- /dev/null +++ b/tools/test-requires @@ -0,0 +1,10 @@ +# Packages needed for dev testing +distribute>=0.6.24 + +coverage +mox +nose +nosexcover +openstack.nose_plugin +pep8 +pylint diff --git a/tox.ini b/tox.ini index 579d32c..e4f4c5e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,36 @@ [tox] -envlist = py26,py27 +envlist = py26,py27,pep8 [testenv] -deps= nose - mox - tissue - openstack.nose_plugin -commands=nosetests [] +setenv = VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/tools/pip-requires + -r{toxinidir}/tools/test-requires +commands = nosetests --where=melange/client/tests/unit + +[testenv:pep8] +deps = pep8 +commands = pep8 --repeat --show-source melange setup.py + +[testenv:pylint] +commands = pylint --rcfile=pylintrc --output-format=parseable melange + +[testenv:cover] +commands = nosetests --where=melange/client/tests/unit --with-coverage --cover-html --cover-erase --cover-package=melange + +[testenv:sdist] +commands = python setup.py sdist {posargs} + +[testenv:hudson] +downloadcache = ~/cache/pip + +[testenv:jenkins26] +basepython = python2.6 +deps = file://{toxinidir}/.cache.bundle + +[testenv:jenkins27] +basepython = python2.7 +deps = file://{toxinidir}/.cache.bundle + +[testenv:jenkinscover] +deps = file://{toxinidir}/.cache.bundle +commands = nosetests --cover-erase --cover-package=melange --with-xcoverage