From 350b9c0b014d1a01ff4b8ddb29e53d831dcbac2f Mon Sep 17 00:00:00 2001 From: hjacobs Date: Wed, 24 Jun 2015 22:41:12 +0200 Subject: [PATCH] #19 add setup.py to distribute PyPI package --- .travis.yml | 5 +- MANIFEST.in | 4 ++ pg_view.py | 110 ++++++++++++++++++++++---------------- setup.py | 127 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_dummy.py | 5 ++ 5 files changed, 202 insertions(+), 49 deletions(-) create mode 100644 MANIFEST.in create mode 100644 setup.py create mode 100644 tests/test_dummy.py diff --git a/.travis.yml b/.travis.yml index 1da8bef..fa1ce8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - "2.7" install: - pip install -r requirements.txt - - pip install flake8 script: - - python -m doctest -v pg_view.py - - flake8 pg_view.py + - python setup.py test + - python setup.py flake8 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c5cfcce --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include *.txt +include *.rst +include *.md +include *.py diff --git a/pg_view.py b/pg_view.py index d939b6b..ef61b99 100644 --- a/pg_view.py +++ b/pg_view.py @@ -39,9 +39,9 @@ try: import psycopg2 import psycopg2.extras + psycopg2_available = True except ImportError: - print('Unable to import psycopg2 module, please, install it (python-psycopg2). Can not continue') - sys.exit(254) + psycopg2_available = False try: import curses curses_available = True @@ -101,55 +101,39 @@ def output_method_is_valid(method): return method in get_valid_output_methods() -# parse command-line options - -parser = OptionParser() -parser.add_option('-v', '--verbose', help='verbose mode', action='store_true', dest='verbose') -parser.add_option('-i', '--instance', help='name of the instance to monitor', action='store', dest='instance') -parser.add_option('-t', '--tick', help='tick length (in seconds)', action='store', dest='tick', type='int', default=1) -parser.add_option('-o', '--output-method', help='send output to the following source', action='store', - default=OUTPUT_METHOD.curses, dest='output_method') -parser.add_option('-V', '--use-version', help='version of the instance to monitor (in case it can\'t be autodetected)', - action='store', dest='version', type='float') -parser.add_option('-l', '--log-file', help='direct log output to the file', action='store', default='pg_view.log', - dest='log_file') -parser.add_option('-R', '--reset-output', help='clear screen after each tick', action='store_true', default=False, - dest='clear_screen') -parser.add_option('-c', '--configuration-file', help='configuration file for PostgreSQL connections', action='store', - default='', dest='config_file') -parser.add_option('-p', '--pid', help='always track a given pid (may be used multiple times)', - action='append', type=int, default=[]) - -options, args = parser.parse_args() +def parse_args(): + '''parse command-line options''' + + parser = OptionParser() + parser.add_option('-v', '--verbose', help='verbose mode', action='store_true', dest='verbose') + parser.add_option('-i', '--instance', help='name of the instance to monitor', action='store', dest='instance') + parser.add_option('-t', '--tick', help='tick length (in seconds)', + action='store', dest='tick', type='int', default=1) + parser.add_option('-o', '--output-method', help='send output to the following source', action='store', + default=OUTPUT_METHOD.curses, dest='output_method') + parser.add_option('-V', '--use-version', + help='version of the instance to monitor (in case it can\'t be autodetected)', + action='store', dest='version', type='float') + parser.add_option('-l', '--log-file', help='direct log output to the file', action='store', default='pg_view.log', + dest='log_file') + parser.add_option('-R', '--reset-output', help='clear screen after each tick', action='store_true', default=False, + dest='clear_screen') + parser.add_option('-c', '--configuration-file', help='configuration file for PostgreSQL connections', + action='store', default='', dest='config_file') + parser.add_option('-p', '--pid', help='always track a given pid (may be used multiple times)', + action='append', type=int, default=[]) + + options, args = parser.parse_args() + return options, args # setup system constants +TICK_LENGTH = 1 -TICK_LENGTH = options.tick - -output_method = options.output_method - -if not output_method_is_valid(output_method): - print('Unsupported output method: {0}'.format(output_method)) - print('Valid output methods are: {0}'.format(','.join(get_valid_output_methods()))) - sys.exit(1) - -if output_method == OUTPUT_METHOD.curses and not curses_available: - print('Curses output is selected, but curses are unavailable, falling back to console output') - output_method == OUTPUT_METHOD.console +output_method = OUTPUT_METHOD.curses -LOG_FILE_NAME = options.log_file +options = None -# truncate the former logs -with open(LOG_FILE_NAME, 'w'): - pass - -# set basic logging -logging.basicConfig(format='%(levelname)s: %(asctime)-15s %(message)s', filename=LOG_FILE_NAME) -logger = logging.getLogger(__name__) -logger.setLevel((logging.INFO if options.verbose else logging.ERROR)) - -log_stderr = logging.StreamHandler() -logger.addHandler(log_stderr) +logger = None class StatCollector(object): @@ -3252,6 +3236,40 @@ def parse_single_line(self, inode): def main(): + global TICK_LENGTH, logger, options + + if not psycopg2_available: + print('Unable to import psycopg2 module, please, install it (python-psycopg2). Can not continue') + sys.exit(254) + + options, args = parse_args() + TICK_LENGTH = options.tick + + output_method = options.output_method + + if not output_method_is_valid(output_method): + print('Unsupported output method: {0}'.format(output_method)) + print('Valid output methods are: {0}'.format(','.join(get_valid_output_methods()))) + sys.exit(1) + + if output_method == OUTPUT_METHOD.curses and not curses_available: + print('Curses output is selected, but curses are unavailable, falling back to console output') + output_method == OUTPUT_METHOD.console + + LOG_FILE_NAME = options.log_file + + # truncate the former logs + with open(LOG_FILE_NAME, 'w'): + pass + + # set basic logging + logging.basicConfig(format='%(levelname)s: %(asctime)-15s %(message)s', filename=LOG_FILE_NAME) + logger = logging.getLogger(__name__) + logger.setLevel((logging.INFO if options.verbose else logging.ERROR)) + + log_stderr = logging.StreamHandler() + logger.addHandler(log_stderr) + user_dbname = options.instance user_dbver = options.version clusters = [] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c421807 --- /dev/null +++ b/setup.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import inspect + +from setuptools.command.test import test as TestCommand +from setuptools import setup + +__location__ = os.path.join(os.getcwd(), os.path.dirname(inspect.getfile(inspect.currentframe()))) + + +def read_module(path): + data = {} + with open(path, 'r') as fd: + exec(fd.read(), data) + return data + +meta = read_module('pg_view.py') +NAME = 'pg-view' +MAIN_MODULE = 'pg_view' +VERSION = meta['__version__'] +DESCRIPTION = 'PostgreSQL Activity View Utility' +LICENSE = 'Apache License 2.0' +URL = 'https://github.com/zalando/pg_view' +author, email = meta['__author__'].rsplit(None, 1) +AUTHOR = author +EMAIL = email.strip('<>') +KEYWORDS = 'postgres postgresql pg database' + +# Add here all kinds of additional classifiers as defined under +# https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: Implementation :: CPython', +] + +CONSOLE_SCRIPTS = ['pg_view = pg_view:main'] + + +class PyTest(TestCommand): + + user_options = [('cov=', None, 'Run coverage'), ('cov-xml=', None, 'Generate junit xml report'), ('cov-html=', + None, 'Generate junit html report'), ('junitxml=', None, 'Generate xml of test results')] + + def initialize_options(self): + TestCommand.initialize_options(self) + self.cov = None + self.cov_xml = False + self.cov_html = False + self.junitxml = None + + def finalize_options(self): + TestCommand.finalize_options(self) + if self.cov is not None: + self.cov = ['--cov', self.cov, '--cov-report', 'term-missing'] + if self.cov_xml: + self.cov.extend(['--cov-report', 'xml']) + if self.cov_html: + self.cov.extend(['--cov-report', 'html']) + + def run_tests(self): + try: + import pytest + except: + raise RuntimeError('py.test is not installed, run: pip install pytest') + params = {'args': self.test_args} + if self.cov: + params['args'] += self.cov + params['plugins'] = ['cov'] + params['args'] += ['--doctest-modules', MAIN_MODULE + '.py', '-s', '-vv'] + errno = pytest.main(**params) + sys.exit(errno) + + +def get_install_requirements(path): + content = open(os.path.join(__location__, path)).read() + return [req for req in content.split('\\n') if req != ''] + + +def read(fname): + return open(os.path.join(__location__, fname)).read() + + +def setup_package(): + # Assemble additional setup commands + cmdclass = {} + cmdclass['test'] = PyTest + + install_reqs = get_install_requirements('requirements.txt') + + command_options = {} + + setup( + name=NAME, + version=VERSION, + url=URL, + description=DESCRIPTION, + author=AUTHOR, + author_email=EMAIL, + license=LICENSE, + keywords=KEYWORDS, + long_description=read('README.md'), + classifiers=CLASSIFIERS, + test_suite='tests', + py_modules=['pg_view'], + packages=[], + install_requires=install_reqs, + setup_requires=['flake8'], + cmdclass=cmdclass, + tests_require=['pytest-cov', 'pytest'], + command_options=command_options, + entry_points={'console_scripts': CONSOLE_SCRIPTS}, + ) + + +if __name__ == '__main__': + setup_package() diff --git a/tests/test_dummy.py b/tests/test_dummy.py new file mode 100644 index 0000000..81bd2ee --- /dev/null +++ b/tests/test_dummy.py @@ -0,0 +1,5 @@ +import pg_view + +def test_dummy(): + pass +