19
19
20
20
from collections import defaultdict
21
21
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
22
29
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
+ ''
24
43
"""
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.
26
64
27
65
INPUT:
28
66
@@ -31,57 +69,104 @@ def find_python_sources(src_dir, modules=['sage']):
31
69
- ``modules`` -- (default: ``['sage']``) sequence of strings:
32
70
the top-level directories in ``src_dir`` to be considered
33
71
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
35
79
36
80
- the list of package names (corresponding to directories with
37
81
``__init__.py``),
38
82
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).
40
86
41
87
Both use dot as separator.
42
88
43
89
EXAMPLES::
44
90
45
91
sage: from sage.env import SAGE_SRC
46
92
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::
54
121
55
122
sage: timeit('find_python_sources(SAGE_SRC)', # random output
56
123
....: number=1, repeat=1)
57
124
1 loops, best of 1: 18.8 ms per loop
58
125
59
126
sage: find_python_sources(SAGE_SRC, modules=['sage_setup'])
60
- (['sage_setup', ...], [...'sage_setup.find'...])
127
+ (['sage_setup', ...], [...'sage_setup.find'...], [] )
61
128
"""
129
+ from distutils .extension import Extension
130
+
62
131
PYMOD_EXT = get_extensions ('source' )[0 ]
63
132
INIT_FILE = '__init__' + PYMOD_EXT
64
133
65
134
python_packages = []
66
135
python_modules = []
136
+ cython_modules = []
67
137
68
138
cwd = os .getcwd ()
69
139
try :
70
140
os .chdir (src_dir )
71
141
for module in modules :
72
142
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 :
74
149
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
+
77
157
for filename in filenames :
78
158
base , ext = os .path .splitext (filename )
79
159
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
+
81
167
finally :
82
168
os .chdir (cwd )
83
- return python_packages , python_modules
84
-
169
+ return python_packages , python_modules , cython_modules
85
170
86
171
def find_extra_files (src_dir , modules , cythonized_dir , special_filenames = []):
87
172
"""
0 commit comments