Source code for builder

''' Build helper for setup script. It includes dependency checks and
monkey-patch in order to extend or modify supporting system software
locally (affecting only the running instance of the program).

.. note:: The original version of this script was
   adapted from `NiPy project <http://nipy.sourceforge.net/>`_.
'''
# Standard module import
import os
import shutil
import glob
import fnmatch
from distutils.cmd import Command
from os.path import join as pjoin, dirname
from distutils.command.clean import clean
from distutils.version import LooseVersion
from distutils.dep_util import newer_group
from distutils.errors import DistutilsError
from numpy.distutils.misc_util import appendpath
from numpy.distutils import log
# PETGEM module import
import petgem.version as INFO
from petgem.base.styles import set_str_format as strFormat


[docs]class NoOptionsDocs(Command): ''' Handler class for no options docs ''' user_options = [('None', None, 'this command has no options')]
[docs] def initialize_options(self): ''' Init options for NoOptionsDocs class. ''' pass
[docs] def finalize_options(self): ''' Finalize options for NoOptionsDocs class. ''' pass
[docs]def get_sphinx_make_command(): ''' Get make command for Sphinx documentation. ''' if os.name in ['posix']: return 'make' elif os.name in ['nt']: return 'make.bat' else: raise ValueError('unsupported system! (%s)' % os.name)
[docs]class SphinxHTMLDocs(NoOptionsDocs): ''' Generate html docs by Sphinx. '''
[docs] def run(self): ''' Run SphinxHTMLDocs class. ''' os.chdir('doc') try: cmd = get_sphinx_make_command() os.system(cmd + ' html') finally: os.chdir('..')
[docs]class SphinxPDFDocs(NoOptionsDocs): ''' Generate pdf docs by Sphinx '''
[docs] def run(self): ''' Run SphinxPDFDocs class. ''' cwd = os.getcwd() os.chdir('doc') try: cmd = get_sphinx_make_command() os.system(cmd + ' latex') os.chdir('build/latex') os.system(cmd + ' all-pdf') os.chdir(cwd) finally: os.chdir(cwd)
[docs]def recursive_glob(top_dir, pattern): ''' Finds all the pathnames matching a specific pattern according to the rules used by Unix shell. ``recursive_glob`` works like ``glob.glob()``, but in working recursively. :param str top_dir: the top-level directory :param str,list pattern: the pattern or list of patterns to match ''' if isinstance(pattern, list): for pat in pattern: for fn in recursive_glob(top_dir, pat): yield fn else: for dirpath, dirnames, filenames in os.walk(top_dir): for fn in [fn for fn in filenames if fnmatch.fnmatchcase(fn, pattern)]: yield os.path.join(dirpath, fn)
[docs]class Clean(clean): ''' Distutils command class to clean. Enhanced to clean also files generated during `python setup.py build_ext --inplace` process. '''
[docs] def run(self): ''' Run clean process recursively for directories: petgem, examples, utils, tests, doc and root directory. ''' clean.run(self) msg = '\nExtra clean:\n' msg = strFormat(msg, FORMAT='OkBlue') print(msg) suffixes = ['*.pyc', '*.o', '*.so', '*.pyd', '*_wrap.c', '*.bak', '*~', '*%', '__pycache__'] for filename in recursive_glob('petgem', suffixes): print(filename) os.remove(filename) for filename in recursive_glob('examples', suffixes): print(filename) os.remove(filename) for filename in recursive_glob('utils', suffixes): print(filename) os.remove(filename) for filename in recursive_glob('tests', suffixes): print(filename) os.remove(filename) for filename in glob.glob('*.pyc'): print(filename) os.remove(filename) for _filename in recursive_glob('petgem', ['*.pyx']): filename = _filename.replace('.pyx', '.c') print(filename) try: os.remove(filename) except OSError: pass filename = _filename.replace('.pyx', '.html') print(filename) try: os.remove(filename) except OSError: pass if os.path.exists('doc/build/html/'): filename = 'doc/build/html/' print(filename) shutil.rmtree('doc/build/html/') if os.path.exists('doc/build/doctrees'): filename = 'doc/build/doctrees' print(filename) shutil.rmtree('doc/build/doctrees') if os.path.exists('doc/build/latex/'): filename = 'doc/build/latex/' print(filename) shutil.rmtree('doc/build/latex/') if os.path.exists('doc/build/doctrees'): filename = 'doc/build/doctrees' print(filename) shutil.rmtree('doc/build/doctrees') if os.path.exists('build'): filename = 'build/' print(filename) shutil.rmtree('build/') directories = ['__pycache__', 'petgem/__pycache__', 'petgem/base/__pycache__', 'petgem/decomposition/__pycache__', 'petgem/efem/__pycache__', 'petgem/mesh/__pycache__', 'petgem/monitor/__pycache__', 'petgem/parallel/__pycache__', 'petgem/postprocessing/__pycache__', 'petgem/solver/__pycache__'] for dir_to_del in directories: if os.path.exists(dir_to_del): filename = dir_to_del print(filename) shutil.rmtree(dir_to_del)
# The command classes for distutils, used by setup.py script. cmdclass = { 'htmldocs': SphinxHTMLDocs, 'pdfdocs': SphinxPDFDocs, 'clean': Clean, }
[docs]def package_check(pkg_name, version=None, optional=False, checker=LooseVersion, version_getter=None, messages=None, show_only=False): ''' Check if package `pkg_name` is present, and in correct version. :param str,list pkg_name: the name of the package as imported into python. Alternative names (e.g. for different versions) may be given in a list. :param str version: the minimum version of the package that is required. If not given, the version is not checked. :param bool optional: if False, raise error for absent package or wrong version, otherwise warning. :param callalble checker: if given, the callable with which to return a comparable thing from a version string. The default is ``distutils.version.LooseVersion``. :param callable version_getter: if given, the callable that takes `pkg_name` as argument, and returns the package version string as in: ``version = version_getter(pkg_name)`` The default is equivalent to: mod = ``__import__(pkg_name); version = mod.__version__`` :param dict messages: if given, the dictionary providing (some of) output messages. :param bool show_only: if True, do not raise exceptions, only show the package name and version information. ''' if version_getter is None: def version_getter(pkg_name): mod = __import__(pkg_name) return mod.__version__ if messages is None: messages = {} msgs = { 'available': '%s is available', 'missing': '%s is missing', 'opt suffix': '; you may get run-time errors', 'version': '%s is available in version %s', 'version old': '%s is available in version %s, but >= %s is needed', 'no version': '%s is available, cannot determine version', } msgs.update(messages) if isinstance(pkg_name, str): names = [pkg_name] else: names = pkg_name import_ok = False for pkg_name in names: try: __import__(pkg_name) except ImportError: pass else: import_ok = True pkg_info = pkg_name + (' (optional)' if optional else '') if not import_ok: if not (optional or show_only): raise RuntimeError(msgs['missing'] % pkg_name) log.warn(msgs['missing'] % pkg_info + msgs['opt suffix']) return if not version: if show_only: log.info(msgs['available'] % pkg_info) return try: have_version = version_getter(pkg_name) except AttributeError: raise RuntimeError(msgs['no version'] % pkg_info) if not have_version: if optional or show_only: log.warn(msgs['no version'] % pkg_info) else: raise RuntimeError(msgs['no version'] % pkg_info) elif checker(have_version) < checker(version): if optional or show_only: log.warn(msgs['version old'] % (pkg_info, have_version, version) + msgs['opt suffix']) else: raise RuntimeError(msgs['version old'] % (pkg_info, have_version, version)) elif show_only: log.info(msgs['version'] % (pkg_info, have_version))
[docs]def unitary_test(): ''' Unitary test for builder.py script. Check if version, top_dir and in_source are present in INFO PETGEM object. ''' from petgem.base.styles import (petgemHeader, petgemFooter) from petgem.base.basis_unit_tests import (test_header, test_footer) petgemHeader() test_header('builder.py') print('Check if version, top_dir and in_source are present' ' in INFO PETGEM object.') pass_test = False success_VERSION = False success_TOP_DIR = False success_IN_SOURCE = False try: if INFO.__version__: success_VERSION = True if INFO.top_dir: success_TOP_DIR = True if INFO.in_source_tree: success_IN_SOURCE = True except: pass if not success_VERSION: msg = ' Could not possible determine PETGEM version.' msg = strFormat(msg, FORMAT='Warning') print(msg) if not success_TOP_DIR: msg = ' Could not possible determine parent directory for PETGEM.' msg = strFormat(msg, FORMAT='Warning') print(msg) if not success_IN_SOURCE: msg = ' Could not possible determine source directory for PETGEM.' msg = strFormat(msg, FORMAT='Warning') print(msg) if success_VERSION and success_TOP_DIR and success_IN_SOURCE: pass_test = True test_footer(pass_test) petgemFooter() return None
if __name__ == '__main__': # Run unitary test unitary_test()