Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
639feee7
Commit
639feee7
authored
Oct 06, 2017
by
Jeroen Demeyer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Verbatim C code using docstring syntax.
parent
e4056355
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
255 additions
and
54 deletions
+255
-54
Cython/Compiler/Code.py
Cython/Compiler/Code.py
+76
-0
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+14
-20
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+11
-12
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+5
-1
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+45
-21
tests/compile/verbatiminclude_cimport.srctree
tests/compile/verbatiminclude_cimport.srctree
+36
-0
tests/run/verbatiminclude.h
tests/run/verbatiminclude.h
+6
-0
tests/run/verbatiminclude.pyx
tests/run/verbatiminclude.pyx
+62
-0
No files found.
Cython/Compiler/Code.py
View file @
639feee7
...
...
@@ -80,6 +80,82 @@ modifier_output_mapper = {
is_self_assignment
=
re
.
compile
(
r" *(\
w+) = (
\1);\
s*$
").match
class IncludeCode(object):
"""
An include file and/or verbatim C code to be included in the
generated sources.
"""
# attributes:
#
# pieces {order: unicode}: pieces of C code to be generated.
# For the included file, the key "
order
" is zero.
# For verbatim include code, the "
order
" is the "
order
"
# attribute of the original IncludeCode where this piece
# of C code was first added. This is needed to prevent
# duplication if the same include code is found through
# multiple cimports.
# location int: where to put this include in the C sources, one
# of the constants INITIAL, EARLY, LATE
# order int: sorting order (automatically set by increasing counter)
# Constants for location. If the same include occurs with different
# locations, the earliest one takes precedense.
INITIAL = 0
EARLY = 1
LATE = 2
counter = 1 # Counter for "
order
"
def __init__(self, include=None, verbatim=None, late=True, initial=False):
self.order = self.counter
type(self).counter += 1
self.pieces = {}
if include:
if include[0] == '<' and include[-1] == '>':
self.pieces[0] = u'#include {0}'.format(include)
late = False # system include is never late
else:
self.pieces[0] = u'#include "
{
0
}
"'.format(include)
if verbatim:
self.pieces[self.order] = verbatim
if initial:
self.location = self.INITIAL
elif late:
self.location = self.LATE
else:
self.location = self.EARLY
def dict_update(self, d, key):
"""
Insert `self` in dict `d` with key `key`. If that key already
exists, update the attributes of the existing value with `self`.
"""
if key in d:
other = d[key]
other.location = min(self.location, other.location)
other.pieces.update(self.pieces)
else:
d[key] = self
def sortkey(self):
return self.order
def mainpiece(self):
"""
Return the main piece of C code, corresponding to the include
file. If there was no include file, return None.
"""
return self.pieces.get(0)
def write(self, code):
# Write values of self.pieces dict, sorted by the keys
for k in sorted(self.pieces):
code.putln(self.pieces[k])
def get_utility_dir():
# make this a function and not global variables:
# http://trac.cython.org/cython_trac/ticket/475
...
...
Cython/Compiler/ModuleNode.py
View file @
639feee7
...
...
@@ -28,7 +28,7 @@ from . import Pythran
from
.Errors
import
error
,
warning
from
.PyrexTypes
import
py_object_type
from
..Utils
import
open_new_file
,
replace_suffix
,
decode_filename
from
.Code
import
UtilityCode
from
.Code
import
UtilityCode
,
IncludeCode
from
.StringEncoding
import
EncodedString
from
.Pythran
import
has_np_pythran
...
...
@@ -86,16 +86,15 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self
.
scope
.
utility_code_list
.
extend
(
scope
.
utility_code_list
)
for
inc
in
scope
.
c_includes
.
values
():
self
.
scope
.
process_include
(
inc
)
def
extend_if_not_in
(
L1
,
L2
):
for
x
in
L2
:
if
x
not
in
L1
:
L1
.
append
(
x
)
extend_if_not_in
(
self
.
scope
.
include_files_early
,
scope
.
include_files_early
)
extend_if_not_in
(
self
.
scope
.
include_files_late
,
scope
.
include_files_late
)
extend_if_not_in
(
self
.
scope
.
included_files
,
scope
.
included_files
)
extend_if_not_in
(
self
.
scope
.
python_include_files
,
scope
.
python_include_files
)
if
merge_scope
:
# Ensure that we don't generate import code for these entries!
...
...
@@ -621,8 +620,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code
.
putln
(
""
)
code
.
putln
(
"#define PY_SSIZE_T_CLEAN"
)
for
filename
in
env
.
python_include_files
:
code
.
putln
(
'#include "%s"'
%
filename
)
for
inc
in
sorted
(
env
.
c_includes
.
values
(),
key
=
IncludeCode
.
sortkey
):
if
inc
.
location
==
inc
.
INITIAL
:
inc
.
write
(
code
)
code
.
putln
(
"#ifndef Py_PYTHON_H"
)
code
.
putln
(
" #error Python headers needed to compile C extensions, "
"please install development version of Python."
)
...
...
@@ -739,19 +739,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def
generate_includes
(
self
,
env
,
cimported_modules
,
code
,
early
=
True
,
late
=
True
):
includes
=
[]
for
inc
in
sorted
(
env
.
c_includes
.
values
(),
key
=
IncludeCode
.
sortkey
):
if
inc
.
location
==
inc
.
EARLY
:
if
early
:
includes
+=
env
.
include_files_early
inc
.
write
(
code
)
elif
inc
.
location
==
inc
.
LATE
:
if
late
:
includes
+=
[
include
for
include
in
env
.
include_files_late
if
include
not
in
env
.
include_files_early
]
for
filename
in
includes
:
byte_decoded_filenname
=
str
(
filename
)
if
byte_decoded_filenname
[
0
]
==
'<'
and
byte_decoded_filenname
[
-
1
]
==
'>'
:
code
.
putln
(
'#include %s'
%
byte_decoded_filenname
)
else
:
code
.
putln
(
'#include "%s"'
%
byte_decoded_filenname
)
inc
.
write
(
code
)
if
early
:
code
.
putln_openmp
(
"#include <omp.h>"
)
...
...
Cython/Compiler/Nodes.py
View file @
639feee7
...
...
@@ -471,6 +471,7 @@ class StatNode(Node):
class
CDefExternNode
(
StatNode
):
# include_file string or None
# verbatim_include string or None
# body StatListNode
child_attrs
=
[
"body"
]
...
...
@@ -480,18 +481,16 @@ class CDefExternNode(StatNode):
env
.
in_cinclude
=
1
self
.
body
.
analyse_declarations
(
env
)
env
.
in_cinclude
=
old_cinclude_flag
inc
=
self
.
include_file
if
inc
:
if
self
.
include_file
or
self
.
verbatim_include
:
# Determine whether include should be late
stats
=
self
.
body
.
stats
if
inc
[
0
]
==
'<'
and
inc
[
-
1
]
==
'>'
:
# System include => always early
env
.
add_include_file
(
inc
)
elif
stats
and
all
(
isinstance
(
node
,
CVarDefNode
)
for
node
in
stats
):
# Generate a late include if the body is not empty and
# all statements are variable or function declarations.
env
.
add_include_file
(
inc
,
late
=
True
)
else
:
env
.
add_include_file
(
inc
)
if
not
stats
:
# Special case: empty 'cdef extern' blocks are early
late
=
False
else
:
late
=
all
(
isinstance
(
node
,
CVarDefNode
)
for
node
in
stats
)
env
.
add_include_file
(
self
.
include_file
,
self
.
verbatim_include
,
late
)
def
analyse_expressions
(
self
,
env
):
return
self
...
...
Cython/Compiler/Parsing.py
View file @
639feee7
...
...
@@ -3081,9 +3081,13 @@ def p_cdef_extern_block(s, pos, ctx):
ctx
.
namespace
=
p_string_literal
(
s
,
'u'
)[
2
]
if
p_nogil
(
s
):
ctx
.
nogil
=
1
body
=
p_suite
(
s
,
ctx
)
# Use "docstring" as verbatim string to include
verbatim_include
,
body
=
p_suite_with_docstring
(
s
,
ctx
,
True
)
return
Nodes
.
CDefExternNode
(
pos
,
include_file
=
include_file
,
verbatim_include
=
verbatim_include
,
body
=
body
,
namespace
=
ctx
.
namespace
)
...
...
Cython/Compiler/Symtab.py
View file @
639feee7
...
...
@@ -1068,9 +1068,8 @@ class ModuleScope(Scope):
# doc string Module doc string
# doc_cname string C name of module doc string
# utility_code_list [UtilityCode] Queuing utility codes for forwarding to Code.py
# python_include_files [string] Standard Python headers to be included
# include_files_early [string] C headers to be included before Cython decls
# include_files_late [string] C headers to be included after Cython decls
# c_includes {key: IncludeCode} C headers or verbatim code to be generated
# See process_include() for more documentation
# string_to_entry {string : Entry} Map string const to entry
# identifier_to_entry {string : Entry} Map identifier string const to entry
# context Context
...
...
@@ -1113,9 +1112,7 @@ class ModuleScope(Scope):
self
.
doc_cname
=
Naming
.
moddoc_cname
self
.
utility_code_list
=
[]
self
.
module_entries
=
{}
self
.
python_include_files
=
[
"Python.h"
]
self
.
include_files_early
=
[]
self
.
include_files_late
=
[]
self
.
c_includes
=
{}
self
.
type_names
=
dict
(
outer_scope
.
type_names
)
self
.
pxd_file_loaded
=
0
self
.
cimported_modules
=
[]
...
...
@@ -1129,6 +1126,7 @@ class ModuleScope(Scope):
for
var_name
in
[
'__builtins__'
,
'__name__'
,
'__file__'
,
'__doc__'
,
'__path__'
,
'__spec__'
,
'__loader__'
,
'__package__'
,
'__cached__'
]:
self
.
declare_var
(
EncodedString
(
var_name
),
py_object_type
,
None
)
self
.
process_include
(
Code
.
IncludeCode
(
"Python.h"
,
initial
=
True
))
def
qualifying_scope
(
self
):
return
self
.
parent_module
...
...
@@ -1251,24 +1249,50 @@ class ModuleScope(Scope):
module
=
module
.
lookup_submodule
(
submodule
)
return
module
def
add_include_file
(
self
,
filename
,
late
=
False
):
if
filename
in
self
.
python_include_files
:
return
# Possibly, the same include appears both as early and as late
# include. We'll deal with this at code generation time.
if
late
:
incs
=
self
.
include_files_late
else
:
incs
=
self
.
include_files_early
if
filename
not
in
incs
:
incs
.
append
(
filename
)
def
add_include_file
(
self
,
filename
,
verbatim_include
=
None
,
late
=
False
):
"""
Add `filename` as include file. Add `verbatim_include` as
verbatim text in the C file.
Both `filename` and `verbatim_include` can be `None` or empty.
"""
inc
=
Code
.
IncludeCode
(
filename
,
verbatim_include
,
late
=
late
)
self
.
process_include
(
inc
)
def
process_include
(
self
,
inc
):
"""
Add `inc`, which is an instance of `IncludeCode`, to this
`ModuleScope`. This either adds a new element to the
`c_includes` dict or it updates an existing entry.
In detail: the values of the dict `self.c_includes` are
instances of `IncludeCode` containing the code to be put in the
generated C file. The keys of the dict are needed to ensure
uniqueness in two ways: if an include file is specified in
multiple "cdef extern" blocks, only one `#include` statement is
generated. Second, the same include might occur multiple times
if we find it through multiple "cimport" paths. So we use the
generated code (of the form `#include "header.h"`) as dict key.
If verbatim code does not belong to any include file (i.e. it
was put in a `cdef extern from *` block), then we use a unique
dict key: namely, the `sortkey()`.
One `IncludeCode` object can contain multiple pieces of C code:
one optional "main piece" for the include file and several other
pieces for the verbatim code. The `IncludeCode.dict_update`
method merges the pieces of two different `IncludeCode` objects
if needed.
"""
key
=
inc
.
mainpiece
()
if
key
is
None
:
key
=
inc
.
sortkey
()
inc
.
dict_update
(
self
.
c_includes
,
key
)
inc
=
self
.
c_includes
[
key
]
def
add_imported_module
(
self
,
scope
):
if
scope
not
in
self
.
cimported_modules
:
for
filename
in
scope
.
include_files_early
:
self
.
add_include_file
(
filename
,
late
=
False
)
for
filename
in
scope
.
include_files_late
:
self
.
add_include_file
(
filename
,
late
=
True
)
for
inc
in
scope
.
c_includes
.
values
():
self
.
process_include
(
inc
)
self
.
cimported_modules
.
append
(
scope
)
for
m
in
scope
.
cimported_modules
:
self
.
add_imported_module
(
m
)
...
...
tests/compile/verbatiminclude_cimport.srctree
0 → 100644
View file @
639feee7
PYTHON setup.py build_ext --inplace
######## setup.py ########
from Cython.Build import cythonize
from distutils.core import setup
setup(
ext_modules = cythonize("*.pyx"),
)
######## test.pyx ########
from moda cimport DEFINE_A
from modb cimport DEFINE_B
######## moda.pxd ########
from verbatim cimport DEFINE_ONCE as DEFINE_A
######## modb.pxd ########
from verbatim cimport DEFINE_ONCE as DEFINE_B
######## verbatim.pxd ########
# Check that we include this only once
cdef extern from *:
"""
#ifdef DEFINE_ONCE
#error "DEFINE_ONCE already defined"
#endif
#define DEFINE_ONCE 1
"""
int DEFINE_ONCE
tests/run/verbatiminclude.h
0 → 100644
View file @
639feee7
static
long
cube
(
long
x
)
{
return
x
*
x
*
x
;
}
#define long broken_long
tests/run/verbatiminclude.pyx
0 → 100644
View file @
639feee7
cdef
extern
from
"verbatiminclude.h"
:
long
cube
(
long
)
cdef
extern
from
*
:
"""
static long square(long x)
{
return x * x;
}
"""
long
square
(
long
)
cdef
extern
from
"verbatiminclude.h"
:
"typedef int myint;"
ctypedef
int
myint
cdef
extern
from
"verbatiminclude.h"
:
"#undef long"
cdef
class
C
:
cdef
myint
val
cdef
extern
from
"Python.h"
:
"""
#define Py_SET_SIZE(obj, size) Py_SIZE((obj)) = (size)
"""
void
Py_SET_SIZE
(
object
,
Py_ssize_t
)
def
test_square
(
x
):
"""
>>> test_square(4)
16
"""
return
square
(
x
)
def
test_cube
(
x
):
"""
>>> test_cube(4)
64
"""
return
cube
(
x
)
def
test_class
():
"""
>>> test_class()
42
"""
cdef
C
x
=
C
()
x
.
val
=
42
return
x
.
val
def
test_set_size
(
x
,
size
):
# This function manipulates Python objects in a bad way, so we
# do not call it. The real test is that it compiles.
Py_SET_SIZE
(
x
,
size
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment