# vim: filetype=python3 tabstop=2 expandtab # blowfish # Copyright (C) 2015 Jashandeep Sohi <jashandeep.s.sohi@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import unittest import sys from pathlib import Path from distutils.command.build_ext import build_ext as org_build_ext from distutils.cmd import Command from distutils.extension import Extension from distutils.errors import DistutilsOptionError, DistutilsFileError try: from Cython.Compiler.Main import compile_single, CompilationOptions except ImportError: HAVE_CYTHON = False else: HAVE_CYTHON = True class CythonExtension(Extension): def __init__( self, name, cython_source, sources = None, output_dir = None, language_level = None, cplus = None, annotate = None, **kwargs ): self.cython_source = Path(cython_source) self.output_dir = Path(output_dir or "cythonized") self.language_level = language_level or 3 self.cplus = cplus or False self.annotate = annotate or False self.output_file = self.output_dir.joinpath( self.cython_source.with_suffix(".cpp" if self.cplus else ".c") ) sources = sources or [] sources.append(str(self.output_file)) super().__init__(name, sources, **kwargs) class build_ext(org_build_ext): def check_newer(self, source, target): if not target.is_file(): return True if source.stat().st_mtime > target.stat().st_mtime: return True return False def build_extensions(self): for ext in self.extensions: if not isinstance(ext, CythonExtension): continue self.cythonize_extension(ext) super().build_extensions() def cythonize_extension(self, ext): if not HAVE_CYTHON: if not ext.output_file.is_file(): raise DistutilsFileError( "expected cythonized file %r not found" % str(ext.output_file) ) else: return is_newer = self.check_newer(ext.cython_source, ext.output_file) if not self.force and not is_newer: return self.announce("cythonizing %r extension" % ext.name, 2) try: ext.output_dir.mkdir(parents=True) except FileExistsError: pass options = CompilationOptions( defaults = None, output_file = str(ext.output_file), language_level = ext.language_level, cplus = ext.cplus, annotate = ext.annotate, ) source = str(ext.cython_source) output_file = str(ext.output_file) self.announce("cythonizing %r -> %r" % (source, output_file), 2) result = compile_single(source, options, ext.name) class test(Command): command_name = "test" description = "run unittests using built modules/packages" user_options = [ ("start-dir=", "s", "directory to start discovery"), ("pattern=", "p", "pattern to match test files"), ("test-name=", "t", "load specific tests using a Python dotted name " "(e.g. pkg.module.Class.function)"), ("buffer", "b", "buffer stdout and stderr during tests"), ("catch", "c", "catch ctrl-C and display results so far"), ("failfast", "f", "stop on first fail or error"), ("exit", "e", "exit if any tests fail") ] boolean_options = ["buffer", "catch", "failfast", "exit"] def initialize_options(self): self.start_dir = None self.pattern = None self.test_name = None self.buffer = False self.catch = False self.failfast = False self.exit = True def finalize_options(self): if self.test_name and (self.start_dir or self.pattern): raise DistutilsOptionError( "must supply either --start-dir/--pattern or --test-name, not both" ) if self.start_dir is None: self.start_dir = "." if self.pattern is None: self.pattern = "test*.py" def run(self): build = self.get_finalized_command("build") build.run() if self.catch: unittest.installHandler() sys.path.insert(0, str(Path(build.build_lib).resolve())) test_loader = unittest.defaultTestLoader if self.test_name: tests = test_loader.loadTestsFromName(self.test_name, None) else: tests = test_loader.discover(self.start_dir, self.pattern) test_runner = unittest.TextTestRunner( verbosity = self.verbose, failfast = self.failfast, buffer = self.buffer ) test_results = test_runner.run(tests) del sys.path[0] if self.exit and not test_results.wasSuccessful(): raise SystemExit()