first commit
This commit is contained in:
commit
417e54da96
5696 changed files with 900003 additions and 0 deletions
|
@ -0,0 +1,142 @@
|
|||
"""
|
||||
Compile a Python script into an executable that embeds CPython and run it.
|
||||
Requires CPython to be built as a shared library ('libpythonX.Y').
|
||||
|
||||
Basic usage:
|
||||
|
||||
python cythonrun somefile.py [ARGS]
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
DEBUG = True
|
||||
|
||||
import sys
|
||||
import os
|
||||
from distutils import sysconfig
|
||||
|
||||
|
||||
def get_config_var(name, default=''):
|
||||
return sysconfig.get_config_var(name) or default
|
||||
|
||||
INCDIR = sysconfig.get_python_inc()
|
||||
LIBDIR1 = get_config_var('LIBDIR')
|
||||
LIBDIR2 = get_config_var('LIBPL')
|
||||
PYLIB = get_config_var('LIBRARY')
|
||||
PYLIB_DYN = get_config_var('LDLIBRARY')
|
||||
if PYLIB_DYN == PYLIB:
|
||||
# no shared library
|
||||
PYLIB_DYN = ''
|
||||
else:
|
||||
PYLIB_DYN = os.path.splitext(PYLIB_DYN[3:])[0] # 'lib(XYZ).so' -> XYZ
|
||||
|
||||
CC = get_config_var('CC', os.environ.get('CC', ''))
|
||||
CFLAGS = get_config_var('CFLAGS') + ' ' + os.environ.get('CFLAGS', '')
|
||||
LINKCC = get_config_var('LINKCC', os.environ.get('LINKCC', CC))
|
||||
LINKFORSHARED = get_config_var('LINKFORSHARED')
|
||||
LIBS = get_config_var('LIBS')
|
||||
SYSLIBS = get_config_var('SYSLIBS')
|
||||
EXE_EXT = sysconfig.get_config_var('EXE')
|
||||
|
||||
def _debug(msg, *args):
|
||||
if DEBUG:
|
||||
if args:
|
||||
msg = msg % args
|
||||
sys.stderr.write(msg + '\n')
|
||||
|
||||
def dump_config():
|
||||
_debug('INCDIR: %s', INCDIR)
|
||||
_debug('LIBDIR1: %s', LIBDIR1)
|
||||
_debug('LIBDIR2: %s', LIBDIR2)
|
||||
_debug('PYLIB: %s', PYLIB)
|
||||
_debug('PYLIB_DYN: %s', PYLIB_DYN)
|
||||
_debug('CC: %s', CC)
|
||||
_debug('CFLAGS: %s', CFLAGS)
|
||||
_debug('LINKCC: %s', LINKCC)
|
||||
_debug('LINKFORSHARED: %s', LINKFORSHARED)
|
||||
_debug('LIBS: %s', LIBS)
|
||||
_debug('SYSLIBS: %s', SYSLIBS)
|
||||
_debug('EXE_EXT: %s', EXE_EXT)
|
||||
|
||||
def runcmd(cmd, shell=True):
|
||||
if shell:
|
||||
cmd = ' '.join(cmd)
|
||||
_debug(cmd)
|
||||
else:
|
||||
_debug(' '.join(cmd))
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
except ImportError: # Python 2.3 ...
|
||||
returncode = os.system(cmd)
|
||||
else:
|
||||
returncode = subprocess.call(cmd, shell=shell)
|
||||
|
||||
if returncode:
|
||||
sys.exit(returncode)
|
||||
|
||||
def clink(basename):
|
||||
runcmd([LINKCC, '-o', basename + EXE_EXT, basename+'.o', '-L'+LIBDIR1, '-L'+LIBDIR2]
|
||||
+ [PYLIB_DYN and ('-l'+PYLIB_DYN) or os.path.join(LIBDIR1, PYLIB)]
|
||||
+ LIBS.split() + SYSLIBS.split() + LINKFORSHARED.split())
|
||||
|
||||
def ccompile(basename):
|
||||
runcmd([CC, '-c', '-o', basename+'.o', basename+'.c', '-I' + INCDIR] + CFLAGS.split())
|
||||
|
||||
def cycompile(input_file, options=()):
|
||||
from ..Compiler import Version, CmdLine, Main
|
||||
options, sources = CmdLine.parse_command_line(list(options or ()) + ['--embed', input_file])
|
||||
_debug('Using Cython %s to compile %s', Version.version, input_file)
|
||||
result = Main.compile(sources, options)
|
||||
if result.num_errors > 0:
|
||||
sys.exit(1)
|
||||
|
||||
def exec_file(program_name, args=()):
|
||||
runcmd([os.path.abspath(program_name)] + list(args), shell=False)
|
||||
|
||||
def build(input_file, compiler_args=(), force=False):
|
||||
"""
|
||||
Build an executable program from a Cython module.
|
||||
|
||||
Returns the name of the executable file.
|
||||
"""
|
||||
basename = os.path.splitext(input_file)[0]
|
||||
exe_file = basename + EXE_EXT
|
||||
if not force and os.path.abspath(exe_file) == os.path.abspath(input_file):
|
||||
raise ValueError("Input and output file names are the same, refusing to overwrite")
|
||||
if (not force and os.path.exists(exe_file) and os.path.exists(input_file)
|
||||
and os.path.getmtime(input_file) <= os.path.getmtime(exe_file)):
|
||||
_debug("File is up to date, not regenerating %s", exe_file)
|
||||
return exe_file
|
||||
cycompile(input_file, compiler_args)
|
||||
ccompile(basename)
|
||||
clink(basename)
|
||||
return exe_file
|
||||
|
||||
def build_and_run(args):
|
||||
"""
|
||||
Build an executable program from a Cython module and runs it.
|
||||
|
||||
Arguments after the module name will be passed verbatimely to the
|
||||
program.
|
||||
"""
|
||||
cy_args = []
|
||||
last_arg = None
|
||||
for i, arg in enumerate(args):
|
||||
if arg.startswith('-'):
|
||||
cy_args.append(arg)
|
||||
elif last_arg in ('-X', '--directive'):
|
||||
cy_args.append(arg)
|
||||
else:
|
||||
input_file = arg
|
||||
args = args[i+1:]
|
||||
break
|
||||
last_arg = arg
|
||||
else:
|
||||
raise ValueError('no input file provided')
|
||||
|
||||
program_name = build(input_file, cy_args)
|
||||
exec_file(program_name, args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
build_and_run(sys.argv[1:])
|
229
kivy_venv/lib/python3.11/site-packages/Cython/Build/Cythonize.py
Normal file
229
kivy_venv/lib/python3.11/site-packages/Cython/Build/Cythonize.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from distutils.core import setup
|
||||
|
||||
from .Dependencies import cythonize, extended_iglob
|
||||
from ..Utils import is_package_dir
|
||||
from ..Compiler import Options
|
||||
|
||||
try:
|
||||
import multiprocessing
|
||||
parallel_compiles = int(multiprocessing.cpu_count() * 1.5)
|
||||
except ImportError:
|
||||
multiprocessing = None
|
||||
parallel_compiles = 0
|
||||
|
||||
|
||||
class _FakePool(object):
|
||||
def map_async(self, func, args):
|
||||
try:
|
||||
from itertools import imap
|
||||
except ImportError:
|
||||
imap=map
|
||||
for _ in imap(func, args):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def terminate(self):
|
||||
pass
|
||||
|
||||
def join(self):
|
||||
pass
|
||||
|
||||
|
||||
def parse_directives(option, name, value, parser):
|
||||
dest = option.dest
|
||||
old_directives = dict(getattr(parser.values, dest,
|
||||
Options.get_directive_defaults()))
|
||||
directives = Options.parse_directive_list(
|
||||
value, relaxed_bool=True, current_settings=old_directives)
|
||||
setattr(parser.values, dest, directives)
|
||||
|
||||
|
||||
def parse_options(option, name, value, parser):
|
||||
dest = option.dest
|
||||
options = dict(getattr(parser.values, dest, {}))
|
||||
for opt in value.split(','):
|
||||
if '=' in opt:
|
||||
n, v = opt.split('=', 1)
|
||||
v = v.lower() not in ('false', 'f', '0', 'no')
|
||||
else:
|
||||
n, v = opt, True
|
||||
options[n] = v
|
||||
setattr(parser.values, dest, options)
|
||||
|
||||
|
||||
def parse_compile_time_env(option, name, value, parser):
|
||||
dest = option.dest
|
||||
old_env = dict(getattr(parser.values, dest, {}))
|
||||
new_env = Options.parse_compile_time_env(value, current_settings=old_env)
|
||||
setattr(parser.values, dest, new_env)
|
||||
|
||||
|
||||
def find_package_base(path):
|
||||
base_dir, package_path = os.path.split(path)
|
||||
while os.path.isfile(os.path.join(base_dir, '__init__.py')):
|
||||
base_dir, parent = os.path.split(base_dir)
|
||||
package_path = '%s/%s' % (parent, package_path)
|
||||
return base_dir, package_path
|
||||
|
||||
|
||||
def cython_compile(path_pattern, options):
|
||||
pool = None
|
||||
all_paths = map(os.path.abspath, extended_iglob(path_pattern))
|
||||
try:
|
||||
for path in all_paths:
|
||||
if options.build_inplace:
|
||||
base_dir = path
|
||||
while not os.path.isdir(base_dir) or is_package_dir(base_dir):
|
||||
base_dir = os.path.dirname(base_dir)
|
||||
else:
|
||||
base_dir = None
|
||||
|
||||
if os.path.isdir(path):
|
||||
# recursively compiling a package
|
||||
paths = [os.path.join(path, '**', '*.{py,pyx}')]
|
||||
else:
|
||||
# assume it's a file(-like thing)
|
||||
paths = [path]
|
||||
|
||||
ext_modules = cythonize(
|
||||
paths,
|
||||
nthreads=options.parallel,
|
||||
exclude_failures=options.keep_going,
|
||||
exclude=options.excludes,
|
||||
compiler_directives=options.directives,
|
||||
compile_time_env=options.compile_time_env,
|
||||
force=options.force,
|
||||
quiet=options.quiet,
|
||||
depfile=options.depfile,
|
||||
**options.options)
|
||||
|
||||
if ext_modules and options.build:
|
||||
if len(ext_modules) > 1 and options.parallel > 1:
|
||||
if pool is None:
|
||||
try:
|
||||
pool = multiprocessing.Pool(options.parallel)
|
||||
except OSError:
|
||||
pool = _FakePool()
|
||||
pool.map_async(run_distutils, [
|
||||
(base_dir, [ext]) for ext in ext_modules])
|
||||
else:
|
||||
run_distutils((base_dir, ext_modules))
|
||||
except:
|
||||
if pool is not None:
|
||||
pool.terminate()
|
||||
raise
|
||||
else:
|
||||
if pool is not None:
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
|
||||
def run_distutils(args):
|
||||
base_dir, ext_modules = args
|
||||
script_args = ['build_ext', '-i']
|
||||
cwd = os.getcwd()
|
||||
temp_dir = None
|
||||
try:
|
||||
if base_dir:
|
||||
os.chdir(base_dir)
|
||||
temp_dir = tempfile.mkdtemp(dir=base_dir)
|
||||
script_args.extend(['--build-temp', temp_dir])
|
||||
setup(
|
||||
script_name='setup.py',
|
||||
script_args=script_args,
|
||||
ext_modules=ext_modules,
|
||||
)
|
||||
finally:
|
||||
if base_dir:
|
||||
os.chdir(cwd)
|
||||
if temp_dir and os.path.isdir(temp_dir):
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser(usage='%prog [options] [sources and packages]+')
|
||||
|
||||
parser.add_option('-X', '--directive', metavar='NAME=VALUE,...',
|
||||
dest='directives', default={}, type="str",
|
||||
action='callback', callback=parse_directives,
|
||||
help='set a compiler directive')
|
||||
parser.add_option('-E', '--compile-time-env', metavar='NAME=VALUE,...',
|
||||
dest='compile_time_env', default={}, type="str",
|
||||
action='callback', callback=parse_compile_time_env,
|
||||
help='set a compile time environment variable')
|
||||
parser.add_option('-s', '--option', metavar='NAME=VALUE',
|
||||
dest='options', default={}, type="str",
|
||||
action='callback', callback=parse_options,
|
||||
help='set a cythonize option')
|
||||
parser.add_option('-2', dest='language_level', action='store_const', const=2, default=None,
|
||||
help='use Python 2 syntax mode by default')
|
||||
parser.add_option('-3', dest='language_level', action='store_const', const=3,
|
||||
help='use Python 3 syntax mode by default')
|
||||
parser.add_option('--3str', dest='language_level', action='store_const', const='3str',
|
||||
help='use Python 3 syntax mode by default')
|
||||
parser.add_option('-a', '--annotate', dest='annotate', action='store_true',
|
||||
help='generate annotated HTML page for source files')
|
||||
|
||||
parser.add_option('-x', '--exclude', metavar='PATTERN', dest='excludes',
|
||||
action='append', default=[],
|
||||
help='exclude certain file patterns from the compilation')
|
||||
|
||||
parser.add_option('-b', '--build', dest='build', action='store_true',
|
||||
help='build extension modules using distutils')
|
||||
parser.add_option('-i', '--inplace', dest='build_inplace', action='store_true',
|
||||
help='build extension modules in place using distutils (implies -b)')
|
||||
parser.add_option('-j', '--parallel', dest='parallel', metavar='N',
|
||||
type=int, default=parallel_compiles,
|
||||
help=('run builds in N parallel jobs (default: %d)' %
|
||||
parallel_compiles or 1))
|
||||
parser.add_option('-f', '--force', dest='force', action='store_true',
|
||||
help='force recompilation')
|
||||
parser.add_option('-q', '--quiet', dest='quiet', action='store_true',
|
||||
help='be less verbose during compilation')
|
||||
|
||||
parser.add_option('--lenient', dest='lenient', action='store_true',
|
||||
help='increase Python compatibility by ignoring some compile time errors')
|
||||
parser.add_option('-k', '--keep-going', dest='keep_going', action='store_true',
|
||||
help='compile as much as possible, ignore compilation failures')
|
||||
parser.add_option('-M', '--depfile', action='store_true', help='produce depfiles for the sources')
|
||||
|
||||
options, args = parser.parse_args(args)
|
||||
if not args:
|
||||
parser.error("no source files provided")
|
||||
if options.build_inplace:
|
||||
options.build = True
|
||||
if multiprocessing is None:
|
||||
options.parallel = 0
|
||||
if options.language_level:
|
||||
assert options.language_level in (2, 3, '3str')
|
||||
options.options['language_level'] = options.language_level
|
||||
return options, args
|
||||
|
||||
|
||||
def main(args=None):
|
||||
options, paths = parse_args(args)
|
||||
|
||||
if options.lenient:
|
||||
# increase Python compatibility by ignoring compile time errors
|
||||
Options.error_on_unknown_names = False
|
||||
Options.error_on_uninitialized = False
|
||||
|
||||
if options.annotate:
|
||||
Options.annotate = True
|
||||
|
||||
for path in paths:
|
||||
cython_compile(path, options)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1293
kivy_venv/lib/python3.11/site-packages/Cython/Build/Dependencies.py
Normal file
1293
kivy_venv/lib/python3.11/site-packages/Cython/Build/Dependencies.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1 @@
|
|||
from Cython.Distutils.build_ext import build_ext
|
376
kivy_venv/lib/python3.11/site-packages/Cython/Build/Inline.py
Normal file
376
kivy_venv/lib/python3.11/site-packages/Cython/Build/Inline.py
Normal file
|
@ -0,0 +1,376 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import hashlib
|
||||
import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from distutils.core import Distribution, Extension
|
||||
from distutils.command.build_ext import build_ext
|
||||
|
||||
import Cython
|
||||
from ..Compiler.Main import Context, default_options
|
||||
|
||||
from ..Compiler.Visitor import CythonTransform, EnvTransform
|
||||
from ..Compiler.ParseTreeTransforms import SkipDeclarations
|
||||
from ..Compiler.TreeFragment import parse_from_strings
|
||||
from ..Compiler.StringEncoding import _unicode
|
||||
from .Dependencies import strip_string_literals, cythonize, cached_function
|
||||
from ..Compiler import Pipeline
|
||||
from ..Utils import get_cython_cache_dir
|
||||
import cython as cython_module
|
||||
|
||||
|
||||
IS_PY3 = sys.version_info >= (3,)
|
||||
|
||||
# A utility function to convert user-supplied ASCII strings to unicode.
|
||||
if not IS_PY3:
|
||||
def to_unicode(s):
|
||||
if isinstance(s, bytes):
|
||||
return s.decode('ascii')
|
||||
else:
|
||||
return s
|
||||
else:
|
||||
to_unicode = lambda x: x
|
||||
|
||||
if sys.version_info < (3, 5):
|
||||
import imp
|
||||
def load_dynamic(name, module_path):
|
||||
return imp.load_dynamic(name, module_path)
|
||||
else:
|
||||
import importlib.util as _importlib_util
|
||||
def load_dynamic(name, module_path):
|
||||
spec = _importlib_util.spec_from_file_location(name, module_path)
|
||||
module = _importlib_util.module_from_spec(spec)
|
||||
# sys.modules[name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
class UnboundSymbols(EnvTransform, SkipDeclarations):
|
||||
def __init__(self):
|
||||
CythonTransform.__init__(self, None)
|
||||
self.unbound = set()
|
||||
def visit_NameNode(self, node):
|
||||
if not self.current_env().lookup(node.name):
|
||||
self.unbound.add(node.name)
|
||||
return node
|
||||
def __call__(self, node):
|
||||
super(UnboundSymbols, self).__call__(node)
|
||||
return self.unbound
|
||||
|
||||
|
||||
@cached_function
|
||||
def unbound_symbols(code, context=None):
|
||||
code = to_unicode(code)
|
||||
if context is None:
|
||||
context = Context([], default_options)
|
||||
from ..Compiler.ParseTreeTransforms import AnalyseDeclarationsTransform
|
||||
tree = parse_from_strings('(tree fragment)', code)
|
||||
for phase in Pipeline.create_pipeline(context, 'pyx'):
|
||||
if phase is None:
|
||||
continue
|
||||
tree = phase(tree)
|
||||
if isinstance(phase, AnalyseDeclarationsTransform):
|
||||
break
|
||||
try:
|
||||
import builtins
|
||||
except ImportError:
|
||||
import __builtin__ as builtins
|
||||
return tuple(UnboundSymbols()(tree) - set(dir(builtins)))
|
||||
|
||||
|
||||
def unsafe_type(arg, context=None):
|
||||
py_type = type(arg)
|
||||
if py_type is int:
|
||||
return 'long'
|
||||
else:
|
||||
return safe_type(arg, context)
|
||||
|
||||
|
||||
def safe_type(arg, context=None):
|
||||
py_type = type(arg)
|
||||
if py_type in (list, tuple, dict, str):
|
||||
return py_type.__name__
|
||||
elif py_type is complex:
|
||||
return 'double complex'
|
||||
elif py_type is float:
|
||||
return 'double'
|
||||
elif py_type is bool:
|
||||
return 'bint'
|
||||
elif 'numpy' in sys.modules and isinstance(arg, sys.modules['numpy'].ndarray):
|
||||
return 'numpy.ndarray[numpy.%s_t, ndim=%s]' % (arg.dtype.name, arg.ndim)
|
||||
else:
|
||||
for base_type in py_type.__mro__:
|
||||
if base_type.__module__ in ('__builtin__', 'builtins'):
|
||||
return 'object'
|
||||
module = context.find_module(base_type.__module__, need_pxd=False)
|
||||
if module:
|
||||
entry = module.lookup(base_type.__name__)
|
||||
if entry.is_type:
|
||||
return '%s.%s' % (base_type.__module__, base_type.__name__)
|
||||
return 'object'
|
||||
|
||||
|
||||
def _get_build_extension():
|
||||
dist = Distribution()
|
||||
# Ensure the build respects distutils configuration by parsing
|
||||
# the configuration files
|
||||
config_files = dist.find_config_files()
|
||||
dist.parse_config_files(config_files)
|
||||
build_extension = build_ext(dist)
|
||||
build_extension.finalize_options()
|
||||
return build_extension
|
||||
|
||||
|
||||
@cached_function
|
||||
def _create_context(cython_include_dirs):
|
||||
return Context(list(cython_include_dirs), default_options)
|
||||
|
||||
|
||||
_cython_inline_cache = {}
|
||||
_cython_inline_default_context = _create_context(('.',))
|
||||
|
||||
|
||||
def _populate_unbound(kwds, unbound_symbols, locals=None, globals=None):
|
||||
for symbol in unbound_symbols:
|
||||
if symbol not in kwds:
|
||||
if locals is None or globals is None:
|
||||
calling_frame = inspect.currentframe().f_back.f_back.f_back
|
||||
if locals is None:
|
||||
locals = calling_frame.f_locals
|
||||
if globals is None:
|
||||
globals = calling_frame.f_globals
|
||||
if symbol in locals:
|
||||
kwds[symbol] = locals[symbol]
|
||||
elif symbol in globals:
|
||||
kwds[symbol] = globals[symbol]
|
||||
else:
|
||||
print("Couldn't find %r" % symbol)
|
||||
|
||||
|
||||
def _inline_key(orig_code, arg_sigs, language_level):
|
||||
key = orig_code, arg_sigs, sys.version_info, sys.executable, language_level, Cython.__version__
|
||||
return hashlib.sha1(_unicode(key).encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def cython_inline(code, get_type=unsafe_type,
|
||||
lib_dir=os.path.join(get_cython_cache_dir(), 'inline'),
|
||||
cython_include_dirs=None, cython_compiler_directives=None,
|
||||
force=False, quiet=False, locals=None, globals=None, language_level=None, **kwds):
|
||||
|
||||
if get_type is None:
|
||||
get_type = lambda x: 'object'
|
||||
ctx = _create_context(tuple(cython_include_dirs)) if cython_include_dirs else _cython_inline_default_context
|
||||
|
||||
cython_compiler_directives = dict(cython_compiler_directives) if cython_compiler_directives else {}
|
||||
if language_level is None and 'language_level' not in cython_compiler_directives:
|
||||
language_level = '3str'
|
||||
if language_level is not None:
|
||||
cython_compiler_directives['language_level'] = language_level
|
||||
|
||||
# Fast path if this has been called in this session.
|
||||
_unbound_symbols = _cython_inline_cache.get(code)
|
||||
if _unbound_symbols is not None:
|
||||
_populate_unbound(kwds, _unbound_symbols, locals, globals)
|
||||
args = sorted(kwds.items())
|
||||
arg_sigs = tuple([(get_type(value, ctx), arg) for arg, value in args])
|
||||
key_hash = _inline_key(code, arg_sigs, language_level)
|
||||
invoke = _cython_inline_cache.get((code, arg_sigs, key_hash))
|
||||
if invoke is not None:
|
||||
arg_list = [arg[1] for arg in args]
|
||||
return invoke(*arg_list)
|
||||
|
||||
orig_code = code
|
||||
code = to_unicode(code)
|
||||
code, literals = strip_string_literals(code)
|
||||
code = strip_common_indent(code)
|
||||
if locals is None:
|
||||
locals = inspect.currentframe().f_back.f_back.f_locals
|
||||
if globals is None:
|
||||
globals = inspect.currentframe().f_back.f_back.f_globals
|
||||
try:
|
||||
_cython_inline_cache[orig_code] = _unbound_symbols = unbound_symbols(code)
|
||||
_populate_unbound(kwds, _unbound_symbols, locals, globals)
|
||||
except AssertionError:
|
||||
if not quiet:
|
||||
# Parsing from strings not fully supported (e.g. cimports).
|
||||
print("Could not parse code as a string (to extract unbound symbols).")
|
||||
|
||||
cimports = []
|
||||
for name, arg in list(kwds.items()):
|
||||
if arg is cython_module:
|
||||
cimports.append('\ncimport cython as %s' % name)
|
||||
del kwds[name]
|
||||
arg_names = sorted(kwds)
|
||||
arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names])
|
||||
key_hash = _inline_key(orig_code, arg_sigs, language_level)
|
||||
module_name = "_cython_inline_" + key_hash
|
||||
|
||||
if module_name in sys.modules:
|
||||
module = sys.modules[module_name]
|
||||
|
||||
else:
|
||||
build_extension = None
|
||||
if cython_inline.so_ext is None:
|
||||
# Figure out and cache current extension suffix
|
||||
build_extension = _get_build_extension()
|
||||
cython_inline.so_ext = build_extension.get_ext_filename('')
|
||||
|
||||
module_path = os.path.join(lib_dir, module_name + cython_inline.so_ext)
|
||||
|
||||
if not os.path.exists(lib_dir):
|
||||
os.makedirs(lib_dir)
|
||||
if force or not os.path.isfile(module_path):
|
||||
cflags = []
|
||||
c_include_dirs = []
|
||||
qualified = re.compile(r'([.\w]+)[.]')
|
||||
for type, _ in arg_sigs:
|
||||
m = qualified.match(type)
|
||||
if m:
|
||||
cimports.append('\ncimport %s' % m.groups()[0])
|
||||
# one special case
|
||||
if m.groups()[0] == 'numpy':
|
||||
import numpy
|
||||
c_include_dirs.append(numpy.get_include())
|
||||
# cflags.append('-Wno-unused')
|
||||
module_body, func_body = extract_func_code(code)
|
||||
params = ', '.join(['%s %s' % a for a in arg_sigs])
|
||||
module_code = """
|
||||
%(module_body)s
|
||||
%(cimports)s
|
||||
def __invoke(%(params)s):
|
||||
%(func_body)s
|
||||
return locals()
|
||||
""" % {'cimports': '\n'.join(cimports),
|
||||
'module_body': module_body,
|
||||
'params': params,
|
||||
'func_body': func_body }
|
||||
for key, value in literals.items():
|
||||
module_code = module_code.replace(key, value)
|
||||
pyx_file = os.path.join(lib_dir, module_name + '.pyx')
|
||||
fh = open(pyx_file, 'w')
|
||||
try:
|
||||
fh.write(module_code)
|
||||
finally:
|
||||
fh.close()
|
||||
extension = Extension(
|
||||
name = module_name,
|
||||
sources = [pyx_file],
|
||||
include_dirs = c_include_dirs,
|
||||
extra_compile_args = cflags)
|
||||
if build_extension is None:
|
||||
build_extension = _get_build_extension()
|
||||
build_extension.extensions = cythonize(
|
||||
[extension],
|
||||
include_path=cython_include_dirs or ['.'],
|
||||
compiler_directives=cython_compiler_directives,
|
||||
quiet=quiet)
|
||||
build_extension.build_temp = os.path.dirname(pyx_file)
|
||||
build_extension.build_lib = lib_dir
|
||||
build_extension.run()
|
||||
|
||||
module = load_dynamic(module_name, module_path)
|
||||
|
||||
_cython_inline_cache[orig_code, arg_sigs, key_hash] = module.__invoke
|
||||
arg_list = [kwds[arg] for arg in arg_names]
|
||||
return module.__invoke(*arg_list)
|
||||
|
||||
|
||||
# Cached suffix used by cython_inline above. None should get
|
||||
# overridden with actual value upon the first cython_inline invocation
|
||||
cython_inline.so_ext = None
|
||||
|
||||
_find_non_space = re.compile('[^ ]').search
|
||||
|
||||
|
||||
def strip_common_indent(code):
|
||||
min_indent = None
|
||||
lines = code.splitlines()
|
||||
for line in lines:
|
||||
match = _find_non_space(line)
|
||||
if not match:
|
||||
continue # blank
|
||||
indent = match.start()
|
||||
if line[indent] == '#':
|
||||
continue # comment
|
||||
if min_indent is None or min_indent > indent:
|
||||
min_indent = indent
|
||||
for ix, line in enumerate(lines):
|
||||
match = _find_non_space(line)
|
||||
if not match or not line or line[indent:indent+1] == '#':
|
||||
continue
|
||||
lines[ix] = line[min_indent:]
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
module_statement = re.compile(r'^((cdef +(extern|class))|cimport|(from .+ cimport)|(from .+ import +[*]))')
|
||||
def extract_func_code(code):
|
||||
module = []
|
||||
function = []
|
||||
current = function
|
||||
code = code.replace('\t', ' ')
|
||||
lines = code.split('\n')
|
||||
for line in lines:
|
||||
if not line.startswith(' '):
|
||||
if module_statement.match(line):
|
||||
current = module
|
||||
else:
|
||||
current = function
|
||||
current.append(line)
|
||||
return '\n'.join(module), ' ' + '\n '.join(function)
|
||||
|
||||
|
||||
try:
|
||||
from inspect import getcallargs
|
||||
except ImportError:
|
||||
def getcallargs(func, *arg_values, **kwd_values):
|
||||
all = {}
|
||||
args, varargs, kwds, defaults = inspect.getargspec(func)
|
||||
if varargs is not None:
|
||||
all[varargs] = arg_values[len(args):]
|
||||
for name, value in zip(args, arg_values):
|
||||
all[name] = value
|
||||
for name, value in list(kwd_values.items()):
|
||||
if name in args:
|
||||
if name in all:
|
||||
raise TypeError("Duplicate argument %s" % name)
|
||||
all[name] = kwd_values.pop(name)
|
||||
if kwds is not None:
|
||||
all[kwds] = kwd_values
|
||||
elif kwd_values:
|
||||
raise TypeError("Unexpected keyword arguments: %s" % list(kwd_values))
|
||||
if defaults is None:
|
||||
defaults = ()
|
||||
first_default = len(args) - len(defaults)
|
||||
for ix, name in enumerate(args):
|
||||
if name not in all:
|
||||
if ix >= first_default:
|
||||
all[name] = defaults[ix - first_default]
|
||||
else:
|
||||
raise TypeError("Missing argument: %s" % name)
|
||||
return all
|
||||
|
||||
|
||||
def get_body(source):
|
||||
ix = source.index(':')
|
||||
if source[:5] == 'lambda':
|
||||
return "return %s" % source[ix+1:]
|
||||
else:
|
||||
return source[ix+1:]
|
||||
|
||||
|
||||
# Lots to be done here... It would be especially cool if compiled functions
|
||||
# could invoke each other quickly.
|
||||
class RuntimeCompiledFunction(object):
|
||||
|
||||
def __init__(self, f):
|
||||
self._f = f
|
||||
self._body = get_body(inspect.getsource(f))
|
||||
|
||||
def __call__(self, *args, **kwds):
|
||||
all = getcallargs(self._f, *args, **kwds)
|
||||
if IS_PY3:
|
||||
return cython_inline(self._body, locals=self._f.__globals__, globals=self._f.__globals__, **all)
|
||||
else:
|
||||
return cython_inline(self._body, locals=self._f.func_globals, globals=self._f.func_globals, **all)
|
|
@ -0,0 +1,565 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
=====================
|
||||
Cython related magics
|
||||
=====================
|
||||
|
||||
Magic command interface for interactive work with Cython
|
||||
|
||||
.. note::
|
||||
|
||||
The ``Cython`` package needs to be installed separately. It
|
||||
can be obtained using ``easy_install`` or ``pip``.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To enable the magics below, execute ``%load_ext cython``.
|
||||
|
||||
``%%cython``
|
||||
|
||||
{CYTHON_DOC}
|
||||
|
||||
``%%cython_inline``
|
||||
|
||||
{CYTHON_INLINE_DOC}
|
||||
|
||||
``%%cython_pyximport``
|
||||
|
||||
{CYTHON_PYXIMPORT_DOC}
|
||||
|
||||
Author:
|
||||
* Brian Granger
|
||||
|
||||
Code moved from IPython and adapted by:
|
||||
* Martín Gaitán
|
||||
|
||||
Parts of this code were taken from Cython.inline.
|
||||
"""
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (C) 2010-2011, IPython Development Team.
|
||||
#
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
#
|
||||
# The full license is in the file ipython-COPYING.rst, distributed with this software.
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import imp
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import copy
|
||||
import distutils.log
|
||||
import textwrap
|
||||
|
||||
IO_ENCODING = sys.getfilesystemencoding()
|
||||
IS_PY2 = sys.version_info[0] < 3
|
||||
|
||||
try:
|
||||
reload
|
||||
except NameError: # Python 3
|
||||
from imp import reload
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
import md5 as hashlib
|
||||
|
||||
from distutils.core import Distribution, Extension
|
||||
from distutils.command.build_ext import build_ext
|
||||
|
||||
from IPython.core import display
|
||||
from IPython.core import magic_arguments
|
||||
from IPython.core.magic import Magics, magics_class, cell_magic
|
||||
try:
|
||||
from IPython.paths import get_ipython_cache_dir
|
||||
except ImportError:
|
||||
# older IPython version
|
||||
from IPython.utils.path import get_ipython_cache_dir
|
||||
from IPython.utils.text import dedent
|
||||
|
||||
from ..Shadow import __version__ as cython_version
|
||||
from ..Compiler.Errors import CompileError
|
||||
from .Inline import cython_inline
|
||||
from .Dependencies import cythonize
|
||||
|
||||
|
||||
PGO_CONFIG = {
|
||||
'gcc': {
|
||||
'gen': ['-fprofile-generate', '-fprofile-dir={TEMPDIR}'],
|
||||
'use': ['-fprofile-use', '-fprofile-correction', '-fprofile-dir={TEMPDIR}'],
|
||||
},
|
||||
# blind copy from 'configure' script in CPython 3.7
|
||||
'icc': {
|
||||
'gen': ['-prof-gen'],
|
||||
'use': ['-prof-use'],
|
||||
}
|
||||
}
|
||||
PGO_CONFIG['mingw32'] = PGO_CONFIG['gcc']
|
||||
|
||||
|
||||
if IS_PY2:
|
||||
def encode_fs(name):
|
||||
return name if isinstance(name, bytes) else name.encode(IO_ENCODING)
|
||||
else:
|
||||
def encode_fs(name):
|
||||
return name
|
||||
|
||||
|
||||
@magics_class
|
||||
class CythonMagics(Magics):
|
||||
|
||||
def __init__(self, shell):
|
||||
super(CythonMagics, self).__init__(shell)
|
||||
self._reloads = {}
|
||||
self._code_cache = {}
|
||||
self._pyximport_installed = False
|
||||
|
||||
def _import_all(self, module):
|
||||
mdict = module.__dict__
|
||||
if '__all__' in mdict:
|
||||
keys = mdict['__all__']
|
||||
else:
|
||||
keys = [k for k in mdict if not k.startswith('_')]
|
||||
|
||||
for k in keys:
|
||||
try:
|
||||
self.shell.push({k: mdict[k]})
|
||||
except KeyError:
|
||||
msg = "'module' object has no attribute '%s'" % k
|
||||
raise AttributeError(msg)
|
||||
|
||||
@cell_magic
|
||||
def cython_inline(self, line, cell):
|
||||
"""Compile and run a Cython code cell using Cython.inline.
|
||||
|
||||
This magic simply passes the body of the cell to Cython.inline
|
||||
and returns the result. If the variables `a` and `b` are defined
|
||||
in the user's namespace, here is a simple example that returns
|
||||
their sum::
|
||||
|
||||
%%cython_inline
|
||||
return a+b
|
||||
|
||||
For most purposes, we recommend the usage of the `%%cython` magic.
|
||||
"""
|
||||
locs = self.shell.user_global_ns
|
||||
globs = self.shell.user_ns
|
||||
return cython_inline(cell, locals=locs, globals=globs)
|
||||
|
||||
@cell_magic
|
||||
def cython_pyximport(self, line, cell):
|
||||
"""Compile and import a Cython code cell using pyximport.
|
||||
|
||||
The contents of the cell are written to a `.pyx` file in the current
|
||||
working directory, which is then imported using `pyximport`. This
|
||||
magic requires a module name to be passed::
|
||||
|
||||
%%cython_pyximport modulename
|
||||
def f(x):
|
||||
return 2.0*x
|
||||
|
||||
The compiled module is then imported and all of its symbols are
|
||||
injected into the user's namespace. For most purposes, we recommend
|
||||
the usage of the `%%cython` magic.
|
||||
"""
|
||||
module_name = line.strip()
|
||||
if not module_name:
|
||||
raise ValueError('module name must be given')
|
||||
fname = module_name + '.pyx'
|
||||
with io.open(fname, 'w', encoding='utf-8') as f:
|
||||
f.write(cell)
|
||||
if 'pyximport' not in sys.modules or not self._pyximport_installed:
|
||||
import pyximport
|
||||
pyximport.install()
|
||||
self._pyximport_installed = True
|
||||
if module_name in self._reloads:
|
||||
module = self._reloads[module_name]
|
||||
# Note: reloading extension modules is not actually supported
|
||||
# (requires PEP-489 reinitialisation support).
|
||||
# Don't know why this should ever have worked as it reads here.
|
||||
# All we really need to do is to update the globals below.
|
||||
#reload(module)
|
||||
else:
|
||||
__import__(module_name)
|
||||
module = sys.modules[module_name]
|
||||
self._reloads[module_name] = module
|
||||
self._import_all(module)
|
||||
|
||||
@magic_arguments.magic_arguments()
|
||||
@magic_arguments.argument(
|
||||
'-a', '--annotate', action='store_true', default=False,
|
||||
help="Produce a colorized HTML version of the source."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-+', '--cplus', action='store_true', default=False,
|
||||
help="Output a C++ rather than C file."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-3', dest='language_level', action='store_const', const=3, default=None,
|
||||
help="Select Python 3 syntax."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-2', dest='language_level', action='store_const', const=2, default=None,
|
||||
help="Select Python 2 syntax."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-f', '--force', action='store_true', default=False,
|
||||
help="Force the compilation of a new module, even if the source has been "
|
||||
"previously compiled."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-c', '--compile-args', action='append', default=[],
|
||||
help="Extra flags to pass to compiler via the `extra_compile_args` "
|
||||
"Extension flag (can be specified multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'--link-args', action='append', default=[],
|
||||
help="Extra flags to pass to linker via the `extra_link_args` "
|
||||
"Extension flag (can be specified multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-l', '--lib', action='append', default=[],
|
||||
help="Add a library to link the extension against (can be specified "
|
||||
"multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-n', '--name',
|
||||
help="Specify a name for the Cython module."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-L', dest='library_dirs', metavar='dir', action='append', default=[],
|
||||
help="Add a path to the list of library directories (can be specified "
|
||||
"multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-I', '--include', action='append', default=[],
|
||||
help="Add a path to the list of include directories (can be specified "
|
||||
"multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'-S', '--src', action='append', default=[],
|
||||
help="Add a path to the list of src files (can be specified "
|
||||
"multiple times)."
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'--pgo', dest='pgo', action='store_true', default=False,
|
||||
help=("Enable profile guided optimisation in the C compiler. "
|
||||
"Compiles the cell twice and executes it in between to generate a runtime profile.")
|
||||
)
|
||||
@magic_arguments.argument(
|
||||
'--verbose', dest='quiet', action='store_false', default=True,
|
||||
help=("Print debug information like generated .c/.cpp file location "
|
||||
"and exact gcc/g++ command invoked.")
|
||||
)
|
||||
@cell_magic
|
||||
def cython(self, line, cell):
|
||||
"""Compile and import everything from a Cython code cell.
|
||||
|
||||
The contents of the cell are written to a `.pyx` file in the
|
||||
directory `IPYTHONDIR/cython` using a filename with the hash of the
|
||||
code. This file is then cythonized and compiled. The resulting module
|
||||
is imported and all of its symbols are injected into the user's
|
||||
namespace. The usage is similar to that of `%%cython_pyximport` but
|
||||
you don't have to pass a module name::
|
||||
|
||||
%%cython
|
||||
def f(x):
|
||||
return 2.0*x
|
||||
|
||||
To compile OpenMP codes, pass the required `--compile-args`
|
||||
and `--link-args`. For example with gcc::
|
||||
|
||||
%%cython --compile-args=-fopenmp --link-args=-fopenmp
|
||||
...
|
||||
|
||||
To enable profile guided optimisation, pass the ``--pgo`` option.
|
||||
Note that the cell itself needs to take care of establishing a suitable
|
||||
profile when executed. This can be done by implementing the functions to
|
||||
optimise, and then calling them directly in the same cell on some realistic
|
||||
training data like this::
|
||||
|
||||
%%cython --pgo
|
||||
def critical_function(data):
|
||||
for item in data:
|
||||
...
|
||||
|
||||
# execute function several times to build profile
|
||||
from somewhere import some_typical_data
|
||||
for _ in range(100):
|
||||
critical_function(some_typical_data)
|
||||
|
||||
In Python 3.5 and later, you can distinguish between the profile and
|
||||
non-profile runs as follows::
|
||||
|
||||
if "_pgo_" in __name__:
|
||||
... # execute critical code here
|
||||
"""
|
||||
args = magic_arguments.parse_argstring(self.cython, line)
|
||||
code = cell if cell.endswith('\n') else cell + '\n'
|
||||
lib_dir = os.path.join(get_ipython_cache_dir(), 'cython')
|
||||
key = (code, line, sys.version_info, sys.executable, cython_version)
|
||||
|
||||
if not os.path.exists(lib_dir):
|
||||
os.makedirs(lib_dir)
|
||||
|
||||
if args.pgo:
|
||||
key += ('pgo',)
|
||||
if args.force:
|
||||
# Force a new module name by adding the current time to the
|
||||
# key which is hashed to determine the module name.
|
||||
key += (time.time(),)
|
||||
|
||||
if args.name:
|
||||
module_name = str(args.name) # no-op in Py3
|
||||
else:
|
||||
module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
|
||||
html_file = os.path.join(lib_dir, module_name + '.html')
|
||||
module_path = os.path.join(lib_dir, module_name + self.so_ext)
|
||||
|
||||
have_module = os.path.isfile(module_path)
|
||||
need_cythonize = args.pgo or not have_module
|
||||
|
||||
if args.annotate:
|
||||
if not os.path.isfile(html_file):
|
||||
need_cythonize = True
|
||||
|
||||
extension = None
|
||||
if need_cythonize:
|
||||
extensions = self._cythonize(module_name, code, lib_dir, args, quiet=args.quiet)
|
||||
if extensions is None:
|
||||
# Compilation failed and printed error message
|
||||
return None
|
||||
assert len(extensions) == 1
|
||||
extension = extensions[0]
|
||||
self._code_cache[key] = module_name
|
||||
|
||||
if args.pgo:
|
||||
self._profile_pgo_wrapper(extension, lib_dir)
|
||||
|
||||
try:
|
||||
self._build_extension(extension, lib_dir, pgo_step_name='use' if args.pgo else None,
|
||||
quiet=args.quiet)
|
||||
except distutils.errors.CompileError:
|
||||
# Build failed and printed error message
|
||||
return None
|
||||
|
||||
module = imp.load_dynamic(module_name, module_path)
|
||||
self._import_all(module)
|
||||
|
||||
if args.annotate:
|
||||
try:
|
||||
with io.open(html_file, encoding='utf-8') as f:
|
||||
annotated_html = f.read()
|
||||
except IOError as e:
|
||||
# File could not be opened. Most likely the user has a version
|
||||
# of Cython before 0.15.1 (when `cythonize` learned the
|
||||
# `force` keyword argument) and has already compiled this
|
||||
# exact source without annotation.
|
||||
print('Cython completed successfully but the annotated '
|
||||
'source could not be read.', file=sys.stderr)
|
||||
print(e, file=sys.stderr)
|
||||
else:
|
||||
return display.HTML(self.clean_annotated_html(annotated_html))
|
||||
|
||||
def _profile_pgo_wrapper(self, extension, lib_dir):
|
||||
"""
|
||||
Generate a .c file for a separate extension module that calls the
|
||||
module init function of the original module. This makes sure that the
|
||||
PGO profiler sees the correct .o file of the final module, but it still
|
||||
allows us to import the module under a different name for profiling,
|
||||
before recompiling it into the PGO optimised module. Overwriting and
|
||||
reimporting the same shared library is not portable.
|
||||
"""
|
||||
extension = copy.copy(extension) # shallow copy, do not modify sources in place!
|
||||
module_name = extension.name
|
||||
pgo_module_name = '_pgo_' + module_name
|
||||
pgo_wrapper_c_file = os.path.join(lib_dir, pgo_module_name + '.c')
|
||||
with io.open(pgo_wrapper_c_file, 'w', encoding='utf-8') as f:
|
||||
f.write(textwrap.dedent(u"""
|
||||
#include "Python.h"
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
extern PyMODINIT_FUNC init%(module_name)s(void);
|
||||
PyMODINIT_FUNC init%(pgo_module_name)s(void); /*proto*/
|
||||
PyMODINIT_FUNC init%(pgo_module_name)s(void) {
|
||||
PyObject *sys_modules;
|
||||
init%(module_name)s(); if (PyErr_Occurred()) return;
|
||||
sys_modules = PyImport_GetModuleDict(); /* borrowed, no exception, "never" fails */
|
||||
if (sys_modules) {
|
||||
PyObject *module = PyDict_GetItemString(sys_modules, "%(module_name)s"); if (!module) return;
|
||||
PyDict_SetItemString(sys_modules, "%(pgo_module_name)s", module);
|
||||
Py_DECREF(module);
|
||||
}
|
||||
}
|
||||
#else
|
||||
extern PyMODINIT_FUNC PyInit_%(module_name)s(void);
|
||||
PyMODINIT_FUNC PyInit_%(pgo_module_name)s(void); /*proto*/
|
||||
PyMODINIT_FUNC PyInit_%(pgo_module_name)s(void) {
|
||||
return PyInit_%(module_name)s();
|
||||
}
|
||||
#endif
|
||||
""" % {'module_name': module_name, 'pgo_module_name': pgo_module_name}))
|
||||
|
||||
extension.sources = extension.sources + [pgo_wrapper_c_file] # do not modify in place!
|
||||
extension.name = pgo_module_name
|
||||
|
||||
self._build_extension(extension, lib_dir, pgo_step_name='gen')
|
||||
|
||||
# import and execute module code to generate profile
|
||||
so_module_path = os.path.join(lib_dir, pgo_module_name + self.so_ext)
|
||||
imp.load_dynamic(pgo_module_name, so_module_path)
|
||||
|
||||
def _cythonize(self, module_name, code, lib_dir, args, quiet=True):
|
||||
pyx_file = os.path.join(lib_dir, module_name + '.pyx')
|
||||
pyx_file = encode_fs(pyx_file)
|
||||
|
||||
c_include_dirs = args.include
|
||||
c_src_files = list(map(str, args.src))
|
||||
if 'numpy' in code:
|
||||
import numpy
|
||||
c_include_dirs.append(numpy.get_include())
|
||||
with io.open(pyx_file, 'w', encoding='utf-8') as f:
|
||||
f.write(code)
|
||||
extension = Extension(
|
||||
name=module_name,
|
||||
sources=[pyx_file] + c_src_files,
|
||||
include_dirs=c_include_dirs,
|
||||
library_dirs=args.library_dirs,
|
||||
extra_compile_args=args.compile_args,
|
||||
extra_link_args=args.link_args,
|
||||
libraries=args.lib,
|
||||
language='c++' if args.cplus else 'c',
|
||||
)
|
||||
try:
|
||||
opts = dict(
|
||||
quiet=quiet,
|
||||
annotate=args.annotate,
|
||||
force=True,
|
||||
)
|
||||
if args.language_level is not None:
|
||||
assert args.language_level in (2, 3)
|
||||
opts['language_level'] = args.language_level
|
||||
elif sys.version_info[0] >= 3:
|
||||
opts['language_level'] = 3
|
||||
return cythonize([extension], **opts)
|
||||
except CompileError:
|
||||
return None
|
||||
|
||||
def _build_extension(self, extension, lib_dir, temp_dir=None, pgo_step_name=None, quiet=True):
|
||||
build_extension = self._get_build_extension(
|
||||
extension, lib_dir=lib_dir, temp_dir=temp_dir, pgo_step_name=pgo_step_name)
|
||||
old_threshold = None
|
||||
try:
|
||||
if not quiet:
|
||||
old_threshold = distutils.log.set_threshold(distutils.log.DEBUG)
|
||||
build_extension.run()
|
||||
finally:
|
||||
if not quiet and old_threshold is not None:
|
||||
distutils.log.set_threshold(old_threshold)
|
||||
|
||||
def _add_pgo_flags(self, build_extension, step_name, temp_dir):
|
||||
compiler_type = build_extension.compiler.compiler_type
|
||||
if compiler_type == 'unix':
|
||||
compiler_cmd = build_extension.compiler.compiler_so
|
||||
# TODO: we could try to call "[cmd] --version" for better insights
|
||||
if not compiler_cmd:
|
||||
pass
|
||||
elif 'clang' in compiler_cmd or 'clang' in compiler_cmd[0]:
|
||||
compiler_type = 'clang'
|
||||
elif 'icc' in compiler_cmd or 'icc' in compiler_cmd[0]:
|
||||
compiler_type = 'icc'
|
||||
elif 'gcc' in compiler_cmd or 'gcc' in compiler_cmd[0]:
|
||||
compiler_type = 'gcc'
|
||||
elif 'g++' in compiler_cmd or 'g++' in compiler_cmd[0]:
|
||||
compiler_type = 'gcc'
|
||||
config = PGO_CONFIG.get(compiler_type)
|
||||
orig_flags = []
|
||||
if config and step_name in config:
|
||||
flags = [f.format(TEMPDIR=temp_dir) for f in config[step_name]]
|
||||
for extension in build_extension.extensions:
|
||||
orig_flags.append((extension.extra_compile_args, extension.extra_link_args))
|
||||
extension.extra_compile_args = extension.extra_compile_args + flags
|
||||
extension.extra_link_args = extension.extra_link_args + flags
|
||||
else:
|
||||
print("No PGO %s configuration known for C compiler type '%s'" % (step_name, compiler_type),
|
||||
file=sys.stderr)
|
||||
return orig_flags
|
||||
|
||||
@property
|
||||
def so_ext(self):
|
||||
"""The extension suffix for compiled modules."""
|
||||
try:
|
||||
return self._so_ext
|
||||
except AttributeError:
|
||||
self._so_ext = self._get_build_extension().get_ext_filename('')
|
||||
return self._so_ext
|
||||
|
||||
def _clear_distutils_mkpath_cache(self):
|
||||
"""clear distutils mkpath cache
|
||||
|
||||
prevents distutils from skipping re-creation of dirs that have been removed
|
||||
"""
|
||||
try:
|
||||
from distutils.dir_util import _path_created
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
_path_created.clear()
|
||||
|
||||
def _get_build_extension(self, extension=None, lib_dir=None, temp_dir=None,
|
||||
pgo_step_name=None, _build_ext=build_ext):
|
||||
self._clear_distutils_mkpath_cache()
|
||||
dist = Distribution()
|
||||
config_files = dist.find_config_files()
|
||||
try:
|
||||
config_files.remove('setup.cfg')
|
||||
except ValueError:
|
||||
pass
|
||||
dist.parse_config_files(config_files)
|
||||
|
||||
if not temp_dir:
|
||||
temp_dir = lib_dir
|
||||
add_pgo_flags = self._add_pgo_flags
|
||||
|
||||
if pgo_step_name:
|
||||
base_build_ext = _build_ext
|
||||
class _build_ext(_build_ext):
|
||||
def build_extensions(self):
|
||||
add_pgo_flags(self, pgo_step_name, temp_dir)
|
||||
base_build_ext.build_extensions(self)
|
||||
|
||||
build_extension = _build_ext(dist)
|
||||
build_extension.finalize_options()
|
||||
if temp_dir:
|
||||
temp_dir = encode_fs(temp_dir)
|
||||
build_extension.build_temp = temp_dir
|
||||
if lib_dir:
|
||||
lib_dir = encode_fs(lib_dir)
|
||||
build_extension.build_lib = lib_dir
|
||||
if extension is not None:
|
||||
build_extension.extensions = [extension]
|
||||
return build_extension
|
||||
|
||||
@staticmethod
|
||||
def clean_annotated_html(html):
|
||||
"""Clean up the annotated HTML source.
|
||||
|
||||
Strips the link to the generated C or C++ file, which we do not
|
||||
present to the user.
|
||||
"""
|
||||
r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
|
||||
html = '\n'.join(l for l in html.splitlines() if not r.match(l))
|
||||
return html
|
||||
|
||||
__doc__ = __doc__.format(
|
||||
# rST doesn't see the -+ flag as part of an option list, so we
|
||||
# hide it from the module-level docstring.
|
||||
CYTHON_DOC=dedent(CythonMagics.cython.__doc__\
|
||||
.replace('-+, --cplus', '--cplus ')),
|
||||
CYTHON_INLINE_DOC=dedent(CythonMagics.cython_inline.__doc__),
|
||||
CYTHON_PYXIMPORT_DOC=dedent(CythonMagics.cython_pyximport.__doc__),
|
||||
)
|
|
@ -0,0 +1,106 @@
|
|||
import difflib
|
||||
import glob
|
||||
import gzip
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import Cython.Build.Dependencies
|
||||
import Cython.Utils
|
||||
from Cython.TestUtils import CythonTest
|
||||
|
||||
|
||||
class TestCyCache(CythonTest):
|
||||
|
||||
def setUp(self):
|
||||
CythonTest.setUp(self)
|
||||
self.temp_dir = tempfile.mkdtemp(
|
||||
prefix='cycache-test',
|
||||
dir='TEST_TMP' if os.path.isdir('TEST_TMP') else None)
|
||||
self.src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir)
|
||||
self.cache_dir = tempfile.mkdtemp(prefix='cache', dir=self.temp_dir)
|
||||
|
||||
def cache_files(self, file_glob):
|
||||
return glob.glob(os.path.join(self.cache_dir, file_glob))
|
||||
|
||||
def fresh_cythonize(self, *args, **kwargs):
|
||||
Cython.Utils.clear_function_caches()
|
||||
Cython.Build.Dependencies._dep_tree = None # discard method caches
|
||||
Cython.Build.Dependencies.cythonize(*args, **kwargs)
|
||||
|
||||
def test_cycache_switch(self):
|
||||
content1 = 'value = 1\n'
|
||||
content2 = 'value = 2\n'
|
||||
a_pyx = os.path.join(self.src_dir, 'a.pyx')
|
||||
a_c = a_pyx[:-4] + '.c'
|
||||
|
||||
open(a_pyx, 'w').write(content1)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
self.assertEqual(1, len(self.cache_files('a.c*')))
|
||||
a_contents1 = open(a_c).read()
|
||||
os.unlink(a_c)
|
||||
|
||||
open(a_pyx, 'w').write(content2)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
a_contents2 = open(a_c).read()
|
||||
os.unlink(a_c)
|
||||
|
||||
self.assertNotEqual(a_contents1, a_contents2, 'C file not changed!')
|
||||
self.assertEqual(2, len(self.cache_files('a.c*')))
|
||||
|
||||
open(a_pyx, 'w').write(content1)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
self.assertEqual(2, len(self.cache_files('a.c*')))
|
||||
a_contents = open(a_c).read()
|
||||
self.assertEqual(
|
||||
a_contents, a_contents1,
|
||||
msg='\n'.join(list(difflib.unified_diff(
|
||||
a_contents.split('\n'), a_contents1.split('\n')))[:10]))
|
||||
|
||||
def test_cycache_uses_cache(self):
|
||||
a_pyx = os.path.join(self.src_dir, 'a.pyx')
|
||||
a_c = a_pyx[:-4] + '.c'
|
||||
open(a_pyx, 'w').write('pass')
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
a_cache = os.path.join(self.cache_dir, os.listdir(self.cache_dir)[0])
|
||||
gzip.GzipFile(a_cache, 'wb').write('fake stuff'.encode('ascii'))
|
||||
os.unlink(a_c)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
a_contents = open(a_c).read()
|
||||
self.assertEqual(a_contents, 'fake stuff',
|
||||
'Unexpected contents: %s...' % a_contents[:100])
|
||||
|
||||
def test_multi_file_output(self):
|
||||
a_pyx = os.path.join(self.src_dir, 'a.pyx')
|
||||
a_c = a_pyx[:-4] + '.c'
|
||||
a_h = a_pyx[:-4] + '.h'
|
||||
a_api_h = a_pyx[:-4] + '_api.h'
|
||||
open(a_pyx, 'w').write('cdef public api int foo(int x): return x\n')
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
expected = [a_c, a_h, a_api_h]
|
||||
for output in expected:
|
||||
self.assertTrue(os.path.exists(output), output)
|
||||
os.unlink(output)
|
||||
self.fresh_cythonize(a_pyx, cache=self.cache_dir)
|
||||
for output in expected:
|
||||
self.assertTrue(os.path.exists(output), output)
|
||||
|
||||
def test_options_invalidation(self):
|
||||
hash_pyx = os.path.join(self.src_dir, 'options.pyx')
|
||||
hash_c = hash_pyx[:-len('.pyx')] + '.c'
|
||||
|
||||
open(hash_pyx, 'w').write('pass')
|
||||
self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False)
|
||||
self.assertEqual(1, len(self.cache_files('options.c*')))
|
||||
|
||||
os.unlink(hash_c)
|
||||
self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=True)
|
||||
self.assertEqual(2, len(self.cache_files('options.c*')))
|
||||
|
||||
os.unlink(hash_c)
|
||||
self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=False)
|
||||
self.assertEqual(2, len(self.cache_files('options.c*')))
|
||||
|
||||
os.unlink(hash_c)
|
||||
self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=True)
|
||||
self.assertEqual(2, len(self.cache_files('options.c*')))
|
|
@ -0,0 +1,96 @@
|
|||
import os, tempfile
|
||||
from Cython.Shadow import inline
|
||||
from Cython.Build.Inline import safe_type
|
||||
from Cython.TestUtils import CythonTest
|
||||
|
||||
try:
|
||||
import numpy
|
||||
has_numpy = True
|
||||
except:
|
||||
has_numpy = False
|
||||
|
||||
test_kwds = dict(force=True, quiet=True)
|
||||
|
||||
global_value = 100
|
||||
|
||||
class TestInline(CythonTest):
|
||||
def setUp(self):
|
||||
CythonTest.setUp(self)
|
||||
self.test_kwds = dict(test_kwds)
|
||||
if os.path.isdir('TEST_TMP'):
|
||||
lib_dir = os.path.join('TEST_TMP','inline')
|
||||
else:
|
||||
lib_dir = tempfile.mkdtemp(prefix='cython_inline_')
|
||||
self.test_kwds['lib_dir'] = lib_dir
|
||||
|
||||
def test_simple(self):
|
||||
self.assertEqual(inline("return 1+2", **self.test_kwds), 3)
|
||||
|
||||
def test_types(self):
|
||||
self.assertEqual(inline("""
|
||||
cimport cython
|
||||
return cython.typeof(a), cython.typeof(b)
|
||||
""", a=1.0, b=[], **self.test_kwds), ('double', 'list object'))
|
||||
|
||||
def test_locals(self):
|
||||
a = 1
|
||||
b = 2
|
||||
self.assertEqual(inline("return a+b", **self.test_kwds), 3)
|
||||
|
||||
def test_globals(self):
|
||||
self.assertEqual(inline("return global_value + 1", **self.test_kwds), global_value + 1)
|
||||
|
||||
def test_no_return(self):
|
||||
self.assertEqual(inline("""
|
||||
a = 1
|
||||
cdef double b = 2
|
||||
cdef c = []
|
||||
""", **self.test_kwds), dict(a=1, b=2.0, c=[]))
|
||||
|
||||
def test_def_node(self):
|
||||
foo = inline("def foo(x): return x * x", **self.test_kwds)['foo']
|
||||
self.assertEqual(foo(7), 49)
|
||||
|
||||
def test_class_ref(self):
|
||||
class Type(object):
|
||||
pass
|
||||
tp = inline("Type")['Type']
|
||||
self.assertEqual(tp, Type)
|
||||
|
||||
def test_pure(self):
|
||||
import cython as cy
|
||||
b = inline("""
|
||||
b = cy.declare(float, a)
|
||||
c = cy.declare(cy.pointer(cy.float), &b)
|
||||
return b
|
||||
""", a=3, **self.test_kwds)
|
||||
self.assertEqual(type(b), float)
|
||||
|
||||
def test_compiler_directives(self):
|
||||
self.assertEqual(
|
||||
inline('return sum(x)',
|
||||
x=[1, 2, 3],
|
||||
cython_compiler_directives={'boundscheck': False}),
|
||||
6
|
||||
)
|
||||
|
||||
def test_lang_version(self):
|
||||
# GH-3419. Caching for inline code didn't always respect compiler directives.
|
||||
inline_divcode = "def f(int a, int b): return a/b"
|
||||
self.assertEqual(
|
||||
inline(inline_divcode, language_level=2)['f'](5,2),
|
||||
2
|
||||
)
|
||||
self.assertEqual(
|
||||
inline(inline_divcode, language_level=3)['f'](5,2),
|
||||
2.5
|
||||
)
|
||||
|
||||
if has_numpy:
|
||||
|
||||
def test_numpy(self):
|
||||
import numpy
|
||||
a = numpy.ndarray((10, 20))
|
||||
a[0,0] = 10
|
||||
self.assertEqual(safe_type(a), 'numpy.ndarray[numpy.float64_t, ndim=2]')
|
||||
self.assertEqual(inline("return a[0,0]", a=a, **self.test_kwds), 10.0)
|
|
@ -0,0 +1,205 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# tag: ipython
|
||||
|
||||
"""Tests for the Cython magics extension."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from Cython.Build import IpythonMagic
|
||||
from Cython.TestUtils import CythonTest
|
||||
|
||||
try:
|
||||
import IPython.testing.globalipapp
|
||||
except ImportError:
|
||||
# Disable tests and fake helpers for initialisation below.
|
||||
def skip_if_not_installed(_):
|
||||
return None
|
||||
else:
|
||||
def skip_if_not_installed(c):
|
||||
return c
|
||||
|
||||
try:
|
||||
# disable IPython history thread before it gets started to avoid having to clean it up
|
||||
from IPython.core.history import HistoryManager
|
||||
HistoryManager.enabled = False
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
code = u"""\
|
||||
def f(x):
|
||||
return 2*x
|
||||
"""
|
||||
|
||||
cython3_code = u"""\
|
||||
def f(int x):
|
||||
return 2 / x
|
||||
|
||||
def call(x):
|
||||
return f(*(x,))
|
||||
"""
|
||||
|
||||
pgo_cython3_code = cython3_code + u"""\
|
||||
def main():
|
||||
for _ in range(100): call(5)
|
||||
main()
|
||||
"""
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
# not using IPython's decorators here because they depend on "nose"
|
||||
try:
|
||||
from unittest import skip as skip_win32
|
||||
except ImportError:
|
||||
# poor dev's silent @unittest.skip()
|
||||
def skip_win32(dummy):
|
||||
def _skip_win32(func):
|
||||
return None
|
||||
return _skip_win32
|
||||
else:
|
||||
def skip_win32(dummy):
|
||||
def _skip_win32(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
func(*args, **kwargs)
|
||||
return wrapper
|
||||
return _skip_win32
|
||||
|
||||
|
||||
@skip_if_not_installed
|
||||
class TestIPythonMagic(CythonTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
CythonTest.setUpClass()
|
||||
cls._ip = IPython.testing.globalipapp.get_ipython()
|
||||
|
||||
def setUp(self):
|
||||
CythonTest.setUp(self)
|
||||
self._ip.extension_manager.load_extension('cython')
|
||||
|
||||
def test_cython_inline(self):
|
||||
ip = self._ip
|
||||
ip.ex('a=10; b=20')
|
||||
result = ip.run_cell_magic('cython_inline', '', 'return a+b')
|
||||
self.assertEqual(result, 30)
|
||||
|
||||
@skip_win32('Skip on Windows')
|
||||
def test_cython_pyximport(self):
|
||||
ip = self._ip
|
||||
module_name = '_test_cython_pyximport'
|
||||
ip.run_cell_magic('cython_pyximport', module_name, code)
|
||||
ip.ex('g = f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
ip.run_cell_magic('cython_pyximport', module_name, code)
|
||||
ip.ex('h = f(-10)')
|
||||
self.assertEqual(ip.user_ns['h'], -20.0)
|
||||
try:
|
||||
os.remove(module_name + '.pyx')
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def test_cython(self):
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '', code)
|
||||
ip.ex('g = f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
|
||||
def test_cython_name(self):
|
||||
# The Cython module named 'mymodule' defines the function f.
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '--name=mymodule', code)
|
||||
# This module can now be imported in the interactive namespace.
|
||||
ip.ex('import mymodule; g = mymodule.f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
|
||||
def test_cython_language_level(self):
|
||||
# The Cython cell defines the functions f() and call().
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '', cython3_code)
|
||||
ip.ex('g = f(10); h = call(10)')
|
||||
if sys.version_info[0] < 3:
|
||||
self.assertEqual(ip.user_ns['g'], 2 // 10)
|
||||
self.assertEqual(ip.user_ns['h'], 2 // 10)
|
||||
else:
|
||||
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0)
|
||||
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0)
|
||||
|
||||
def test_cython3(self):
|
||||
# The Cython cell defines the functions f() and call().
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '-3', cython3_code)
|
||||
ip.ex('g = f(10); h = call(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0)
|
||||
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0)
|
||||
|
||||
def test_cython2(self):
|
||||
# The Cython cell defines the functions f() and call().
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '-2', cython3_code)
|
||||
ip.ex('g = f(10); h = call(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 2 // 10)
|
||||
self.assertEqual(ip.user_ns['h'], 2 // 10)
|
||||
|
||||
@skip_win32('Skip on Windows')
|
||||
def test_cython3_pgo(self):
|
||||
# The Cython cell defines the functions f() and call().
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '-3 --pgo', pgo_cython3_code)
|
||||
ip.ex('g = f(10); h = call(10); main()')
|
||||
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0)
|
||||
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0)
|
||||
|
||||
@skip_win32('Skip on Windows')
|
||||
def test_extlibs(self):
|
||||
ip = self._ip
|
||||
code = u"""
|
||||
from libc.math cimport sin
|
||||
x = sin(0.0)
|
||||
"""
|
||||
ip.user_ns['x'] = 1
|
||||
ip.run_cell_magic('cython', '-l m', code)
|
||||
self.assertEqual(ip.user_ns['x'], 0)
|
||||
|
||||
|
||||
def test_cython_verbose(self):
|
||||
ip = self._ip
|
||||
ip.run_cell_magic('cython', '--verbose', code)
|
||||
ip.ex('g = f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
|
||||
def test_cython_verbose_thresholds(self):
|
||||
@contextmanager
|
||||
def mock_distutils():
|
||||
class MockLog:
|
||||
DEBUG = 1
|
||||
INFO = 2
|
||||
thresholds = [INFO]
|
||||
|
||||
def set_threshold(self, val):
|
||||
self.thresholds.append(val)
|
||||
return self.thresholds[-2]
|
||||
|
||||
|
||||
new_log = MockLog()
|
||||
old_log = IpythonMagic.distutils.log
|
||||
try:
|
||||
IpythonMagic.distutils.log = new_log
|
||||
yield new_log
|
||||
finally:
|
||||
IpythonMagic.distutils.log = old_log
|
||||
|
||||
ip = self._ip
|
||||
with mock_distutils() as verbose_log:
|
||||
ip.run_cell_magic('cython', '--verbose', code)
|
||||
ip.ex('g = f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
self.assertEqual([verbose_log.INFO, verbose_log.DEBUG, verbose_log.INFO],
|
||||
verbose_log.thresholds)
|
||||
|
||||
with mock_distutils() as normal_log:
|
||||
ip.run_cell_magic('cython', '', code)
|
||||
ip.ex('g = f(10)')
|
||||
self.assertEqual(ip.user_ns['g'], 20.0)
|
||||
self.assertEqual([normal_log.INFO], normal_log.thresholds)
|
|
@ -0,0 +1,57 @@
|
|||
from Cython.Build.Dependencies import strip_string_literals
|
||||
|
||||
from Cython.TestUtils import CythonTest
|
||||
|
||||
class TestStripLiterals(CythonTest):
|
||||
|
||||
def t(self, before, expected):
|
||||
actual, literals = strip_string_literals(before, prefix="_L")
|
||||
self.assertEqual(expected, actual)
|
||||
for key, value in literals.items():
|
||||
actual = actual.replace(key, value)
|
||||
self.assertEqual(before, actual)
|
||||
|
||||
def test_empty(self):
|
||||
self.t("", "")
|
||||
|
||||
def test_single_quote(self):
|
||||
self.t("'x'", "'_L1_'")
|
||||
|
||||
def test_double_quote(self):
|
||||
self.t('"x"', '"_L1_"')
|
||||
|
||||
def test_nested_quotes(self):
|
||||
self.t(""" '"' "'" """, """ '_L1_' "_L2_" """)
|
||||
|
||||
def test_triple_quote(self):
|
||||
self.t(" '''a\n''' ", " '''_L1_''' ")
|
||||
|
||||
def test_backslash(self):
|
||||
self.t(r"'a\'b'", "'_L1_'")
|
||||
self.t(r"'a\\'", "'_L1_'")
|
||||
self.t(r"'a\\\'b'", "'_L1_'")
|
||||
|
||||
def test_unicode(self):
|
||||
self.t("u'abc'", "u'_L1_'")
|
||||
|
||||
def test_raw(self):
|
||||
self.t(r"r'abc\\'", "r'_L1_'")
|
||||
|
||||
def test_raw_unicode(self):
|
||||
self.t(r"ru'abc\\'", "ru'_L1_'")
|
||||
|
||||
def test_comment(self):
|
||||
self.t("abc # foo", "abc #_L1_")
|
||||
|
||||
def test_comment_and_quote(self):
|
||||
self.t("abc # 'x'", "abc #_L1_")
|
||||
self.t("'abc#'", "'_L1_'")
|
||||
|
||||
def test_include(self):
|
||||
self.t("include 'a.pxi' # something here",
|
||||
"include '_L1_' #_L2_")
|
||||
|
||||
def test_extern(self):
|
||||
self.t("cdef extern from 'a.h': # comment",
|
||||
"cdef extern from '_L1_': #_L2_")
|
||||
|
|
@ -0,0 +1 @@
|
|||
# empty file
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
from .Dependencies import cythonize
|
||||
from .Distutils import build_ext
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue