Skip to content

Commit b582789

Browse files
committed
src/sage_setup/find.py: Filter by directive 'sage_setup: distribution = PKG', find Cython modules
1 parent 0295c8f commit b582789

File tree

2 files changed

+105
-20
lines changed

2 files changed

+105
-20
lines changed

src/sage_setup/find.py

+103-18
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,48 @@
1919

2020
from collections import defaultdict
2121

22+
def read_distribution(src_file):
23+
"""
24+
Parse ``src_file`` for a ``# sage_setup: distribution = PKG`` directive.
25+
26+
INPUT:
27+
28+
- ``src_file`` -- file name of a Python or Cython source file
2229
23-
def find_python_sources(src_dir, modules=['sage']):
30+
OUTPUT:
31+
32+
- a string, the name of the distribution package (``PKG``); or the empty
33+
string if no directive was found.
34+
35+
EXAMPLES::
36+
37+
sage: from sage.env import SAGE_SRC
38+
sage: from sage_setup.find import read_distribution
39+
sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'tdlib.pyx'))
40+
'sage-tdlib'
41+
sage: read_distribution(os.path.join(SAGE_SRC, 'sage', 'graphs', 'graph_decompositions', 'modular_decomposition.py'))
42+
''
2443
"""
25-
Find all Python packages and Python modules in the sources.
44+
from Cython.Utils import open_source_file
45+
with open_source_file(src_file, error_handling='ignore') as fh:
46+
for line in fh:
47+
# Adapted from Cython's Build/Dependencies.py
48+
line = line.lstrip()
49+
if not line:
50+
continue
51+
if line[0] != '#':
52+
break
53+
line = line[1:].lstrip()
54+
kind = "sage_setup:"
55+
if line.startswith(kind):
56+
key, _, value = [s.strip() for s in line[len(kind):].partition('=')]
57+
if key == "distribution":
58+
return value
59+
return ''
60+
61+
def find_python_sources(src_dir, modules=['sage'], distributions=None):
62+
"""
63+
Find all Python packages and Python/Cython modules in the sources.
2664
2765
INPUT:
2866
@@ -31,57 +69,104 @@ def find_python_sources(src_dir, modules=['sage']):
3169
- ``modules`` -- (default: ``['sage']``) sequence of strings:
3270
the top-level directories in ``src_dir`` to be considered
3371
34-
OUTPUT: Pair consisting of
72+
- ``distributions`` -- (default: ``None``) if not ``None``,
73+
should be a sequence or set of strings: only find modules whose
74+
``distribution`` (from a ``# sage_setup: distribution = PACKAGE``
75+
directive in the module source file) is an element of
76+
``distributions``.
77+
78+
OUTPUT: Triple consisting of
3579
3680
- the list of package names (corresponding to directories with
3781
``__init__.py``),
3882
39-
- module names (corresponding to other ``*.py`` files).
83+
- Python module names (corresponding to other ``*.py`` files).
84+
85+
- Cython extensions (corresponding to ``*.pyx`` files).
4086
4187
Both use dot as separator.
4288
4389
EXAMPLES::
4490
4591
sage: from sage.env import SAGE_SRC
4692
sage: from sage_setup.find import find_python_sources
47-
sage: py_packages, py_modules = find_python_sources(SAGE_SRC)
48-
sage: examples = ['sage.structure', 'sage.structure.formal_sum',
49-
....: 'sage.structure.sage_object', 'sage.doctest.tests']
50-
sage: [m in py_packages for m in examples]
51-
[True, False, False, False]
52-
sage: [m in py_modules for m in examples]
53-
[False, True, False, False]
93+
sage: py_packages, py_modules, cy_modules = find_python_sources(SAGE_SRC)
94+
95+
Ordinary package (with ``__init__.py``)::
96+
97+
sage: ['sage.structure' in L for L in (py_packages, py_modules)]
98+
[True, False]
99+
100+
Python module in an ordinary package::
101+
102+
sage: ['sage.structure.formal_sum' in L for L in (py_packages, py_modules)]
103+
[False, True]
104+
105+
Cython module in an ordinary package::
106+
107+
sage: ['sage.structure.sage_object' in L for L in (py_packages, py_modules)]
108+
[False, False]
109+
110+
Subdirectory without any Python files::
111+
112+
sage: ['sage.doctest.tests' in L for L in (py_packages, py_modules)]
113+
[False, False]
114+
115+
Filtering by distribution (distutils package)::
116+
117+
sage: find_python_sources(SAGE_SRC, distributions=['sage-tdlib'])
118+
([], [], [<distutils.extension.Extension('sage.graphs.graph_decompositions.tdlib')...>])
119+
120+
Benchmarking::
54121
55122
sage: timeit('find_python_sources(SAGE_SRC)', # random output
56123
....: number=1, repeat=1)
57124
1 loops, best of 1: 18.8 ms per loop
58125
59126
sage: find_python_sources(SAGE_SRC, modules=['sage_setup'])
60-
(['sage_setup', ...], [...'sage_setup.find'...])
127+
(['sage_setup', ...], [...'sage_setup.find'...], [])
61128
"""
129+
from distutils.extension import Extension
130+
62131
PYMOD_EXT = get_extensions('source')[0]
63132
INIT_FILE = '__init__' + PYMOD_EXT
64133

65134
python_packages = []
66135
python_modules = []
136+
cython_modules = []
67137

68138
cwd = os.getcwd()
69139
try:
70140
os.chdir(src_dir)
71141
for module in modules:
72142
for dirpath, dirnames, filenames in os.walk(module):
73-
if INIT_FILE not in filenames:
143+
package = dirpath.replace(os.path.sep, '.')
144+
if INIT_FILE in filenames:
145+
# Ordinary package.
146+
if distributions is None or '' in distributions:
147+
python_packages.append(package)
148+
else:
74149
continue
75-
dirpath = dirpath.replace(os.path.sep, '.')
76-
python_packages.append(dirpath)
150+
151+
def is_in_distributions(filename):
152+
if distributions is None:
153+
return True
154+
distribution = read_distribution(os.path.join(dirpath, filename))
155+
return distribution in distributions
156+
77157
for filename in filenames:
78158
base, ext = os.path.splitext(filename)
79159
if ext == PYMOD_EXT and base != '__init__':
80-
python_modules.append(dirpath + '.' + base)
160+
if is_in_distributions(filename):
161+
python_modules.append(package + '.' + base)
162+
if ext == '.pyx':
163+
if is_in_distributions(filename):
164+
cython_modules.append(Extension(package + '.' + base,
165+
sources=[os.path.join(dirpath, filename)]))
166+
81167
finally:
82168
os.chdir(cwd)
83-
return python_packages, python_modules
84-
169+
return python_packages, python_modules, cython_modules
85170

86171
def find_extra_files(src_dir, modules, cythonized_dir, special_filenames=[]):
87172
"""

src/setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
print("Discovering Python/Cython source code....")
6464
t = time.time()
6565
from sage_setup.find import find_python_sources
66-
python_packages, python_modules = find_python_sources(
66+
python_packages, python_modules, cython_modules = find_python_sources(
6767
SAGE_SRC, ['sage', 'sage_setup'])
6868

6969
log.debug('python_packages = {0}'.format(python_packages))
@@ -117,4 +117,4 @@
117117
build_cython=sage_build_cython,
118118
build_ext=sage_build_ext,
119119
install=sage_install),
120-
ext_modules = ext_modules)
120+
ext_modules = ext_modules + cython_modules)

0 commit comments

Comments
 (0)