-
-
Notifications
You must be signed in to change notification settings - Fork 745
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
Setuptools packaging improvments #1647
Changes from 6 commits
036177d
93a1baa
159d797
4d973be
d3dec45
48a07a6
a51c4f2
68b0826
09593f8
d43f06d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
eventlet>=0.13.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
# 1. Don't include forget to populate spcific requirements.txt. IMORTANT!!! | ||
# 2. Don't include setuptools into any of requirements.txt (we don't want it bundled). | ||
apscheduler>=3.0.0rc1 | ||
eventlet>=0.13.0 | ||
flask | ||
|
@@ -15,19 +17,18 @@ python-dateutil | |
python-json-logger | ||
pyyaml | ||
requests | ||
setuptools==11.1 | ||
six==1.9.0 | ||
tooz | ||
git+https://github.com/StackStorm/[email protected] | ||
git+https://github.com/StackStorm/fabric.git@stanley-patched | ||
git+https://github.com/StackStorm/[email protected]#egg=python-mistralclient | ||
git+https://github.com/StackStorm/fabric.git@stanley-patched#egg=fabric | ||
passlib>=1.6.2,<1.7 | ||
lockfile>=0.10.2,<0.11 | ||
python-gnupg>=0.3.7,<0.4 | ||
jsonpath-rw>=1.3.0 | ||
# Requirements for linux pack | ||
# used by file watcher sensor | ||
pyinotify>=0.9.5,<=0.10 | ||
-e git+https://github.com/Kami/logshipper.git@stackstorm_patched#egg=logshipper | ||
git+https://github.com/Kami/logshipper.git@stackstorm_patched#egg=logshipper | ||
# used by nmap actions | ||
python-nmap>=0.3.4,<0.4 | ||
semver>=2.1.2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
#!/usr/bin/env python | ||
# Licensed to the StackStorm, Inc ('StackStorm') under one or more | ||
# contributor license agreements. See the NOTICE file distributed with | ||
# this work for additional information regarding copyright ownership. | ||
# The ASF licenses this file to You 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. | ||
|
||
""" | ||
This script is used to automate generation of requirements.txt | ||
for st2 components. | ||
|
||
The idea behind this script is that that each component has it's own requirements | ||
in-requirements.txt file (input requirements file). Except this file | ||
there's also the top-level fixed-requirements.txt which pins production versions | ||
for the whole st2 stack. During production use (building, packaging, etc) | ||
requirements.txt is generated from in-requirements.txt where version of packages are | ||
fixed according to fixed-requirements.txt. | ||
""" | ||
|
||
import argparse | ||
import os | ||
import os.path | ||
import sys | ||
from distutils.version import StrictVersion | ||
|
||
OSCWD = os.path.abspath(os.curdir) | ||
GET_PIP = ' curl https://bootstrap.pypa.io/get-pip.py | python' | ||
|
||
try: | ||
import pip | ||
from pip.req import parse_requirements | ||
except ImportError: | ||
print 'Download pip:\n', GET_PIP | ||
sys.exit(1) | ||
|
||
|
||
def parse_args(): | ||
parser = argparse.ArgumentParser(description='Tool for requirements.txt generation.') | ||
parser.add_argument('-s', '--source-requirements', nargs='+', | ||
required=True, | ||
help='Specifiy paths to requirements file(s). ' | ||
'In case severasl requirements files are given their content is merged.') | ||
parser.add_argument('-f', '--fixed-requirements', required=True, | ||
help='Specifiy path to fixed-requirements.txt file.') | ||
parser.add_argument('-o', '--output-file', default='requirements.txt', | ||
help='Specifiy path to the resulting requirements file.') | ||
if len(sys.argv) < 2: | ||
parser.print_help() | ||
sys.exit(1) | ||
return vars(parser.parse_args()) | ||
|
||
|
||
def check_pip_version(): | ||
if StrictVersion(pip.__version__) < StrictVersion('6.0.0'): | ||
print "Upgrade pip, your version `{}' "\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Can you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah sure. Got it. Ruby had known distinctions between ' and ", that's why On Tue, Jun 23, 2015, 07:58 Lakshmi Kannan [email protected] wrote:
|
||
"is outdated:\n".format(pip.__version__), GET_PIP | ||
sys.exit(1) | ||
|
||
|
||
def load_requirements(file_path): | ||
return tuple((r for r in parse_requirements(file_path, session=False))) | ||
|
||
|
||
def locate_file(path, must_exist=False): | ||
if not os.path.isabs(path): | ||
path = os.path.join(OSCWD, path) | ||
if must_exist and not os.path.isfile(path): | ||
print("Error: couldn't locate file `{}'".format(path)) | ||
return path | ||
|
||
|
||
def merge_source_requirements(sources): | ||
"""Read requirements source files and merge it's content. | ||
""" | ||
projects = set() | ||
merged_requirements = [] | ||
for infile_path in (locate_file(p, must_exist=True) for p in sources): | ||
for req in load_requirements(infile_path): | ||
# Requirements lines like "project[version_spec]" | ||
if req.req: | ||
# Skip already added project name | ||
if req.req.project_name in projects: | ||
continue | ||
projects.add(req.req.project_name) | ||
merged_requirements.append(req) | ||
|
||
# Requirements lines like "vcs+proto://url" | ||
elif req.link: | ||
merged_requirements.append(req) | ||
else: | ||
raise RuntimeError('Unexpected requirement {}'.format(req)) | ||
|
||
return merged_requirements | ||
|
||
|
||
def write_requirements(sources=None, fixed_requirements=None, output_file=None): | ||
"""Wrire resulting requirements taking versions from the fixed_requirements. | ||
""" | ||
requirements = merge_source_requirements(sources) | ||
fixed = load_requirements(locate_file(fixed_requirements, must_exist=True)) | ||
fixedreq_hash = {req.req.project_name: req for req in fixed if req.req} | ||
|
||
with open(output_file, 'w') as f: | ||
f.write("# Don't edit this file. It's generated automatically!\n") | ||
for req in requirements: | ||
# we don't have any idea how to process links, so just add them | ||
if req.link: | ||
rline = str(req.link) | ||
else: | ||
project = req.req.project_name | ||
if project in fixedreq_hash: | ||
rline = str(fixedreq_hash[project]) | ||
else: | ||
rline = str(req.req) | ||
f.write(rline + '\n') | ||
|
||
return | ||
|
||
|
||
if __name__ == '__main__': | ||
check_pip_version() | ||
args = parse_args() | ||
write_requirements(sources=args['source_requirements'], | ||
fixed_requirements=args['fixed_requirements'], | ||
output_file=args['output_file']) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.10dev0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
eventlet>=0.13.0 | ||
jsonschema>=2.3.0 | ||
kombu | ||
mongoengine>=0.8.7,<0.9 | ||
oslo.config | ||
pecan==0.7.0 | ||
six==1.9.0 |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,27 +14,43 @@ | |
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
try: | ||
from setuptools import setup, find_packages | ||
except ImportError: | ||
from ez_setup import use_setuptools | ||
use_setuptools() | ||
from setuptools import setup, find_packages | ||
import re | ||
import os.path | ||
from pip.req import parse_requirements | ||
from setuptools import setup, find_packages | ||
|
||
|
||
def fetch_requirements(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
See tweepy/tweepy#533 for an example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edit: Nvm, I didn't see the full diff. You already use that function :) On a related note - we just need to make sure we use pip >= 6.0 since older versions don't take |
||
links = [] | ||
reqs = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this short form for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some requirements are represented as git+proto//URL@branch#some_egg_dep or The links array is given to setup as a separate argument (if I'm not wrong The reqs array contains On Tue, Jun 23, 2015, 08:01 Lakshmi Kannan [email protected] wrote:
|
||
for req in parse_requirements('requirements.txt', session=False): | ||
if req.link: | ||
links.append(str(req.link)) | ||
reqs.append(str(req.req)) | ||
return (reqs, links) | ||
|
||
|
||
current_dir = os.path.dirname(os.path.realpath(__file__)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, why was this removed?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've removed that since, install_requires is not needed as we can use On Mon, Jun 22, 2015 at 2:46 PM, Tomaz Muraus
|
||
version_file = os.path.join(current_dir, '../st2client/st2client/__init__.py') | ||
with open(version_file, 'r') as f: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make this a bit more robust - can we just temporary add from st2client import __version__
version = __version__.split('.') This should work as long as init.py in st2client doesn't depend on other external dependencies (which is a bad practice anyway). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry but we can't use it as well as my method with parsing file. This limit is due to our potential tooling when wheel is created (or egg) - pip is run and component directory copied under some '/tmp/$RANDOM$' path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added top There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC, dict comprehensions are only available in 2.7 and above and we might
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In short - for now we still run tests and other lint tasks under Python 2.6 so the easiest / best thing to do is to just replace dict comprehension with When we decide to drop support for 2.6 we can re-evaluate that. |
||
vmatch = re.search(r'__version__ = [\'\"](.*)[\'\"]$', f.read(), flags=re.MULTILINE) | ||
|
||
|
||
install_reqs, dep_links = fetch_requirements() | ||
ST2_COMPONENT = os.path.basename(current_dir) | ||
ST2_VERSION = vmatch.group(1) | ||
|
||
|
||
setup( | ||
name='st2api', | ||
version='0.4.0', | ||
description='', | ||
name=ST2_COMPONENT, | ||
version=ST2_VERSION, | ||
description='{} component'.format(ST2_COMPONENT), | ||
author='StackStorm', | ||
author_email='[email protected]', | ||
install_requires=[ | ||
"pecan", | ||
], | ||
package_data={ | ||
'st2api': ['templates/*.html'] | ||
}, | ||
test_suite='st2api', | ||
install_requires=install_reqs, | ||
dependency_links=dep_links, | ||
test_suite=ST2_COMPONENT, | ||
zip_safe=False, | ||
include_package_data=True, | ||
packages=find_packages(exclude=['ez_setup']) | ||
packages=find_packages(exclude=['setuptools']) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
eventlet>=0.13.0 | ||
oslo.config | ||
passlib>=1.6.2,<1.7 | ||
pecan==0.7.0 | ||
pymongo<3.0 | ||
six==1.9.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
prettytable | ||
python-dateutil | ||
pyyaml | ||
jsonpath-rw>=1.3.0 | ||
requests | ||
six | ||
python-dateutil | ||
jsonpath-rw | ||
six==1.9.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# https://docs.python.org/2/distutils/sourcedist.html#commands | ||
# Include all files under the source tree by default. | ||
# Other behaviour can be used though. | ||
recursive-include st2common *.* * | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Kami, @lakshmi-kannan there was a question why I have removed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of just including all in the manifest, I would prefer to explicitly declare all the non-code artifacts we want to include here - so things like README, LICENSE, NOTICE, requirements.txt, etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, so far I suggest like this. Later on more comprehensive filtering can On Thu, Jun 25, 2015, 08:37 Tomaz Muraus [email protected] wrote:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still confused by that line - "st2common/{,.*}" basically means everything under st2common/st2common. In any case, like I said above, I would prefer to be explicit and collect Python modules and packages we want to include inside setup.py and only explicitly declare other non-package files we want to include here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's explicit, all stuff under st2common/st2common. That's package_data works quite implicitly... And |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,3 +32,6 @@ deb: | |
tar --transform=s~^~$(COMPONENTS)-$(VER)/~ --exclude=correlation -czf ~/$(COMPONENTS).tar.gz bin st2 logrotate.d $(COMPONENTS) ../contrib ../docs ../tools/ ../requirements.txt packaging/debian | ||
pushd ~ && tar -xzf $(COMPONENTS).tar.gz && cd $(COMPONENTS)-$(VER) && cp -Rf packaging/debian ./ && dpkg-buildpackage -us -uc -b && popd | ||
cp -f ~/$(COMPONENT)*.deb ~/debbuild/ | ||
|
||
.PHONY: requirements | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be in the top level Makefile? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, it will be as we progress. [WIP] so far There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And yeah in top level |
||
python ../scripts/fixate-requirements.py -s in-requirements.txt -f ../fixed-requirements.txt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this is how it should work in prod, our tooling must refresh requirement.txt, this makefile line does so. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
python-dateutil | ||
eventlet>=0.13.0 | ||
git+https://github.com/StackStorm/fabric.git@stanley-patched#egg=fabric | ||
jinja2 | ||
jsonschema>=2.3.0 | ||
kombu | ||
mongoengine>=0.8.7,<0.9 | ||
oslo.config | ||
paramiko | ||
pecan==0.7.0 | ||
pyyaml | ||
requests | ||
semver>=2.1.2 | ||
six==1.9.0 | ||
tooz |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,24 +14,45 @@ | |
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
try: | ||
from setuptools import setup, find_packages | ||
except ImportError: | ||
from ez_setup import use_setuptools | ||
use_setuptools() | ||
from setuptools import setup, find_packages | ||
# Just setup pip before using with a command: | ||
# curl https://bootstrap.pypa.io/get-pip.py | python | ||
|
||
import os.path | ||
from pip.req import parse_requirements | ||
from setuptools import setup, find_packages | ||
|
||
|
||
def fetch_requirements(): | ||
links = [] | ||
reqs = [] | ||
for req in parse_requirements('requirements.txt', session=False): | ||
if req.link: | ||
links.append(str(req.link)) | ||
reqs.append(str(req.req)) | ||
return (reqs, links) | ||
|
||
current_dir = os.path.dirname(os.path.realpath(__file__)) | ||
with open(os.path.join(current_dir, 'st2_version'), 'r') as f: | ||
st2_version = f.read().strip() | ||
|
||
install_reqs, dep_links = fetch_requirements() | ||
st2_component = os.path.basename(current_dir) | ||
|
||
|
||
setup( | ||
name='st2common', | ||
version='0.4.0', | ||
description='', | ||
name=st2_component, | ||
version=st2_version, | ||
description='{} component'.format(st2_component), | ||
author='StackStorm', | ||
author_email='[email protected]', | ||
install_requires=[ | ||
"pecan", | ||
], | ||
test_suite='st2common', | ||
install_requires=install_reqs, | ||
dependency_links=dep_links, | ||
test_suite=st2_component, | ||
zip_safe=False, | ||
include_package_data=True, | ||
packages=find_packages(exclude=['ez_setup']) | ||
packages=find_packages(exclude=['setuptools', 'examples', 'tests']), | ||
scripts=[ | ||
'bin/st2-bootstrap-rmq', | ||
'bin/st2-register-content' | ||
] | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
python-gnupg>=0.3.7,<0.4 | ||
requests | ||
six==1.9.0 | ||
pyyaml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
apscheduler>=3.0.0rc1 | ||
python-dateutil | ||
eventlet>=0.13.0 | ||
jsonpath-rw>=1.3.0 | ||
jsonschema>=2.3.0 | ||
kombu | ||
oslo.config | ||
six==1.9.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I missed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: typo.
specific
.IMPORTANT
.Nit: Sentence isn't making sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha-ha. You are right) This sentence will likely disappear from here soon,
let's ignore it meanwhile)
On Tue, Jun 23, 2015, 07:53 Lakshmi Kannan [email protected] wrote: