initial creation
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
The original Pyrex code as of 2006-04 is licensed under the following
|
||||
license: "Copyright stuff: Pyrex is free of restrictions. You may use,
|
||||
redistribute, modify and distribute modified versions."
|
||||
|
||||
------------------
|
||||
|
||||
Cython, which derives from Pyrex, is licensed under the Apache 2.0
|
||||
Software License. More precisely, all modifications and new code
|
||||
made to go from Pyrex to Cython are so licensed.
|
||||
|
||||
See LICENSE.txt for more details.
|
||||
|
||||
------------------
|
||||
|
||||
The output of a Cython compilation is NOT considered a derivative
|
||||
work of Cython. Specifically, though the compilation process may
|
||||
embed snippets of varying lengths into the final output, these
|
||||
snippets, as embedded in the output, do not encumber the resulting
|
||||
output with any license restrictions.
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,57 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: Cython
|
||||
Version: 0.29.32
|
||||
Summary: The Cython compiler for writing C extensions for the Python language.
|
||||
Home-page: http://cython.org/
|
||||
Author: Robert Bradshaw, Stefan Behnel, Dag Seljebotn, Greg Ewing, et al.
|
||||
Author-email: cython-devel@python.org
|
||||
License: Apache
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: Apache Software License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Programming Language :: C
|
||||
Classifier: Programming Language :: Cython
|
||||
Classifier: Topic :: Software Development :: Code Generators
|
||||
Classifier: Topic :: Software Development :: Compilers
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
|
||||
License-File: LICENSE.txt
|
||||
License-File: COPYING.txt
|
||||
|
||||
The Cython language makes writing C extensions for the Python language as
|
||||
easy as Python itself. Cython is a source code translator based on Pyrex_,
|
||||
but supports more cutting edge functionality and optimizations.
|
||||
|
||||
The Cython language is a superset of the Python language (almost all Python
|
||||
code is also valid Cython code), but Cython additionally supports optional
|
||||
static typing to natively call C functions, operate with C++ classes and
|
||||
declare fast C types on variables and class attributes. This allows the
|
||||
compiler to generate very efficient C code from Cython code.
|
||||
|
||||
This makes Cython the ideal language for writing glue code for external
|
||||
C/C++ libraries, and for fast C modules that speed up the execution of
|
||||
Python code.
|
||||
|
||||
Note that for one-time builds, e.g. for CI/testing, on platforms that are not
|
||||
covered by one of the wheel packages provided on PyPI *and* the pure Python wheel
|
||||
that we provide is not used, it is substantially faster than a full source build
|
||||
to install an uncompiled (slower) version of Cython with::
|
||||
|
||||
pip install Cython --install-option="--no-cython-compile"
|
||||
|
||||
.. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/
|
||||
@@ -0,0 +1,425 @@
|
||||
../../Scripts/cygdb.exe,sha256=R4pwLf9emspPw5cK8nC2v0hw-yXwzMhUyhKXpQt2cOs,106355
|
||||
../../Scripts/cython.exe,sha256=LEaEPo994CmKmR20iF7k_pRLGHJglMKGibwjsCvT3m4,106376
|
||||
../../Scripts/cythonize.exe,sha256=C2Y3QWiIeAmFrm0hRrxGZV6Sp3XNWO_VSoEK5nCGqN8,106356
|
||||
Cython-0.29.32.dist-info/COPYING.txt,sha256=4escSahQjoFz2sMBV-SmQ5pErYhGGUdGxCT7w_wrldc,756
|
||||
Cython-0.29.32.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
Cython-0.29.32.dist-info/LICENSE.txt,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
||||
Cython-0.29.32.dist-info/METADATA,sha256=wIbpLr1hGP2GCk9qjCkP7QW1eg-f3SytJ7BcU7_UtR0,2767
|
||||
Cython-0.29.32.dist-info/RECORD,,
|
||||
Cython-0.29.32.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
Cython-0.29.32.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
|
||||
Cython-0.29.32.dist-info/entry_points.txt,sha256=VU8NX8gnQyFbyqiWMzfh9BHvYMuoQRS3Nbm3kKcKQeY,139
|
||||
Cython-0.29.32.dist-info/top_level.txt,sha256=jLV8tZV98iCbIfiJR4DVzTX5Ru1Y_pYMZ59wkMCe6SY,24
|
||||
Cython/Build/BuildExecutable.py,sha256=9wjcOncQpbCT_Pu2Ljj5jufFLSY6T_oHOFt0uXYTdRk,4318
|
||||
Cython/Build/Cythonize.py,sha256=OBSutgd93PXiRi5_6GRuhpfMZehw9YOfRgt1mxi6QLk,8359
|
||||
Cython/Build/Dependencies.py,sha256=SkMflfSEXLlyGdkQ0xLMY7zMMoBxq4VgfPyN_BUSvTQ,50391
|
||||
Cython/Build/Distutils.py,sha256=iO5tPX84Kc-ZWMocfuQbl_PqyC9HGGIRS-NiKI60-ZE,49
|
||||
Cython/Build/Inline.py,sha256=wd5_xBOup8MWxNtv5uwWgWWG5GrkShYysMMluY7ripE,13451
|
||||
Cython/Build/IpythonMagic.py,sha256=j-E-JJYGj03ceWDp5SRmyK-WxyroRkNOEQODA9rCfFc,21126
|
||||
Cython/Build/Tests/TestCyCache.py,sha256=olOvphv4q1CLsNzMAhgmXnL77zhGGQKuKbYs_iSFFvA,4151
|
||||
Cython/Build/Tests/TestInline.py,sha256=bp4XGXZYSyxY2zXI0q4bg58a0ARsclUzXSfWykn0dZw,2854
|
||||
Cython/Build/Tests/TestIpythonMagic.py,sha256=H88J6_r3dP5V3-N7eSKZuE2l9G8BtkFF_o8wY7pifKU,6225
|
||||
Cython/Build/Tests/TestStripLiterals.py,sha256=-QeUd22OnoL50rW2EgpfIA01UzRMutcBA5NrhkHiE7M,1550
|
||||
Cython/Build/Tests/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Build/Tests/__pycache__/TestCyCache.cpython-310.pyc,,
|
||||
Cython/Build/Tests/__pycache__/TestInline.cpython-310.pyc,,
|
||||
Cython/Build/Tests/__pycache__/TestIpythonMagic.cpython-310.pyc,,
|
||||
Cython/Build/Tests/__pycache__/TestStripLiterals.cpython-310.pyc,,
|
||||
Cython/Build/Tests/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Build/__init__.py,sha256=zBhW6hT9Mwk1ZybfuPi61iCa6A4srId1HJz9OiEd07o,69
|
||||
Cython/Build/__pycache__/BuildExecutable.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/Cythonize.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/Dependencies.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/Distutils.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/Inline.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/IpythonMagic.cpython-310.pyc,,
|
||||
Cython/Build/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/CodeWriter.py,sha256=Sa1hLmUcIMnCGTNASqyzfGqk70i6v-YslD5ogllCaiY,23901
|
||||
Cython/Compiler/AnalysedTreeTransforms.py,sha256=T2r1SLFeG7a4D9tt93hm8YRH_aGLwmK82PNpb1RsbnE,3826
|
||||
Cython/Compiler/Annotate.py,sha256=bkVgdH3ItuIbaSpi9Qo5bKiIjOX3-J5sZORfcyk_eZY,12950
|
||||
Cython/Compiler/AutoDocTransforms.py,sha256=le7k-xhGWDkvpRXRppZsqhJzkav6i41GmAOmDetxVmk,7517
|
||||
Cython/Compiler/Buffer.py,sha256=6-YIUiLWtgpC2LRhs7116umzNiEbzA6cz1lnPkSEn30,29035
|
||||
Cython/Compiler/Builtin.py,sha256=fdR58NDTWNQWO3tA0JtAzUq9JfXL6pJdEOSHTcz6BZA,22469
|
||||
Cython/Compiler/CmdLine.py,sha256=iqjwt2Vauv6LMZJRxX3mQ6tv63woZMNpUhYdga6tJuc,10668
|
||||
Cython/Compiler/Code.pxd,sha256=XEZU44jXCOFoYTG15-R0X41krTrjpuo1wxRKjyzYwoY,3354
|
||||
Cython/Compiler/Code.py,sha256=sOh_jtyZaRsT38QKKXS1wojqYeVa_u7XSlIr94B1_A8,97242
|
||||
Cython/Compiler/CodeGeneration.py,sha256=jkcx2uX07nck0UZSgysIThRuJiPbdkSeXR4Z2uzbQU8,1108
|
||||
Cython/Compiler/CythonScope.py,sha256=mNwmE509uePmR3S2djg3Dq6zOZ3hgK-U8NDeawys9WM,6027
|
||||
Cython/Compiler/DebugFlags.py,sha256=5Zg9ETp0qPFEma6QMtrGUwu9Fn6NTYMBMWPI_GxFW0A,623
|
||||
Cython/Compiler/Errors.py,sha256=GATz9x6onls09cM6TeDw3kdBgdCxUiKJBILwukBF6WI,7554
|
||||
Cython/Compiler/ExprNodes.py,sha256=ytuNzxI3T5orTO10YfmCYw410w49CB--VEzaUzWqgic,549591
|
||||
Cython/Compiler/FlowControl.pxd,sha256=W8bqGCJLzvAhnL3d1OF8798ZDJg0QI0eA_ebnA4dkoQ,2918
|
||||
Cython/Compiler/FlowControl.py,sha256=-I33Yd9sp1RCENjFkrufBM8Xrj3y5SI7KZ04Vz5Djiw,45981
|
||||
Cython/Compiler/FusedNode.py,sha256=qmHVHylEPpllK_x-471xBG-zMzAt0RF52tLxt_-RZqs,37794
|
||||
Cython/Compiler/Future.py,sha256=GwcWZ_Vti0atfbOARfS2kIvZOvRuPu38wbShIn4o4kA,587
|
||||
Cython/Compiler/Interpreter.py,sha256=iNweexX2HDI5nZj2rzkW-lw9Rq3gzM__P7SBqH3uxbU,2106
|
||||
Cython/Compiler/Lexicon.py,sha256=Cw_wIfQymcTEdkoo82V2xbV8kvCp30O-Pc7qF4hbfCI,4855
|
||||
Cython/Compiler/Main.py,sha256=PLc04GL5UFxxF4_kA6Pzgmg-97ItXmbnlulbxyLA0GY,36795
|
||||
Cython/Compiler/MemoryView.py,sha256=c6J7PtQ6wccb9uBxvbLngia4jO-h2uea7viIzJNhDYU,30009
|
||||
Cython/Compiler/ModuleNode.py,sha256=dcC6JoKNlN0-W2hIeyH8gHxcnaxL3LMRembzBmoRsn4,141982
|
||||
Cython/Compiler/Naming.py,sha256=Z6FLRoOl21F91SMoIoMSu_CqYosGhxDJLO9grYSHhVI,6333
|
||||
Cython/Compiler/Nodes.py,sha256=-1_SeKkQ3ijDWshDk6ge0TgiBP4NnwdhNWlU9EI37xM,391550
|
||||
Cython/Compiler/Optimize.py,sha256=MJ5w9bXi6qU-yvDaiiZdBMSnp8g-QhEhBhs1zpWVICQ,210296
|
||||
Cython/Compiler/Options.py,sha256=ryrLc_NNo0QSMaP-jHhmo1L2oZJA6h7KpE6xs_KSyUo,19707
|
||||
Cython/Compiler/ParseTreeTransforms.pxd,sha256=oDSda3XYP79o8tCAxEm_epWWVPXSaPy8lYrprWYRyNk,2468
|
||||
Cython/Compiler/ParseTreeTransforms.py,sha256=oLSgT3GwVZQ3wLE1mr0B5x-cJDyG-MpKLyPsYAbKnag,139345
|
||||
Cython/Compiler/Parsing.pxd,sha256=pL_EQdVWaw7EZVTZrxzMcUvoSJeAoXXPRh8kCLudysk,8984
|
||||
Cython/Compiler/Parsing.py,sha256=W4bes0PFrpuOl5MzEBrKj0yvFqvrnnPboZ0IytkjbeI,130020
|
||||
Cython/Compiler/Pipeline.py,sha256=6ravd0QCR5sCoKlz9HEz209A2UqgLp4Qp0VysoKa_mI,14061
|
||||
Cython/Compiler/PyrexTypes.py,sha256=-nGJxm5amgIjkfvBpQFeSLn8zS2fi_cBvOtgBIfr5F8,173958
|
||||
Cython/Compiler/Pythran.py,sha256=NHIml0yx0jPLyTLRAHXZr0LHTyEyfYqspgYuV4vdNKI,7267
|
||||
Cython/Compiler/Scanning.pxd,sha256=vjjPLZb5udPzMpk67DKojTTDUl31QU86oXyAMks7Hsw,2113
|
||||
Cython/Compiler/Scanning.py,sha256=Gl7sU5rI-5H5v8z9QLZBh1hivS2cOGa9H878QdEpUU4,18438
|
||||
Cython/Compiler/StringEncoding.py,sha256=dn3jVSL4JScbeYf1f56j5RJR9M58AnCDdJObC-cc3fg,10812
|
||||
Cython/Compiler/Symtab.py,sha256=n0tdTnbPkSuU5EgPkr6nGBN98Lhbklbjt8o6NUFt264,111727
|
||||
Cython/Compiler/Tests/TestBuffer.py,sha256=yw-KUu2pu4CnsqYaqxj5rpyYodmjJSEqUvQNxLwPL8I,4155
|
||||
Cython/Compiler/Tests/TestCmdLine.py,sha256=mT673jQ9MekvTExZdnQLztNabCsAMRSuN__egQ7rZ9A,6658
|
||||
Cython/Compiler/Tests/TestFlowControl.py,sha256=ge3iqBor6xe5MLaLbOtw7ETntJnAh8EequF1aetVzMw,1848
|
||||
Cython/Compiler/Tests/TestGrammar.py,sha256=zWMvYG19nIH85Le8ragXt2vLBlWlGGNeMgrTdQO5JGM,3443
|
||||
Cython/Compiler/Tests/TestMemView.py,sha256=yBAQ5tN8DVPTFRJ81dAzOepCt5Ly6fiaAssQve5ryy4,2515
|
||||
Cython/Compiler/Tests/TestParseTreeTransforms.py,sha256=xiRKOWqHIMIT1bbYB1o5TefKJXC8oPpjt79tQFodMRc,8900
|
||||
Cython/Compiler/Tests/TestSignatureMatching.py,sha256=qMiQZeg5_Eu8VfCY_lMawqpjpKNV0r6p6-9czKec1aY,3338
|
||||
Cython/Compiler/Tests/TestStringEncoding.py,sha256=RL1YDXrOUe1sPLEbWmTJQ5VF-uEZ_KLz0jaeQoMx85k,2315
|
||||
Cython/Compiler/Tests/TestTreeFragment.py,sha256=wHlnF0ApwxeITx9pzg46P9N_2lM-7mrPwhDeNlIQnXM,2206
|
||||
Cython/Compiler/Tests/TestTreePath.py,sha256=x-2KBIhSE6-vT-2BPe2q-zaa1oHtc42ibKzVs_y8_So,4238
|
||||
Cython/Compiler/Tests/TestTypes.py,sha256=YuFib5WCJfSPafrhy5yrCUdwajYw61yGPo4HveTyzUs,669
|
||||
Cython/Compiler/Tests/TestUtilityLoad.py,sha256=Uzf4_bOjha-zwQaikNbsAOVQs3ZPX3YD7QQ5T4s66YY,3341
|
||||
Cython/Compiler/Tests/TestVisitor.py,sha256=QAnBpUhnirSFKqXWiawo-OhXhxIRTQidWxEzGjJDz6M,2228
|
||||
Cython/Compiler/Tests/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Compiler/Tests/__pycache__/TestBuffer.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestCmdLine.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestFlowControl.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestGrammar.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestMemView.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestParseTreeTransforms.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestSignatureMatching.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestStringEncoding.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestTreeFragment.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestTreePath.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestTypes.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestUtilityLoad.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/TestVisitor.cpython-310.pyc,,
|
||||
Cython/Compiler/Tests/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Compiler/TreeFragment.py,sha256=jQn4Lp2dNddJ-tjPquoFcyTcX9EIuTAbZKZAKs9-cGU,9408
|
||||
Cython/Compiler/TreePath.py,sha256=3_lScMAd2Sly2ekZ8HO8dyZstGSruINl2MXXq9OYd2Q,7641
|
||||
Cython/Compiler/TypeInference.py,sha256=s-GKZcq16KPPgY_OpF8cTlQmX1Cpu-qBMCtmAYDg8fc,22326
|
||||
Cython/Compiler/TypeSlots.py,sha256=zvQrFMKa6Pzk8jB187PuqFhdJUa82SQ1thztSV1ArhI,37837
|
||||
Cython/Compiler/UtilNodes.py,sha256=mS6jlZ530p17WGU0ApbwvLecuByT18LFipVrKJg5jrM,11636
|
||||
Cython/Compiler/UtilityCode.py,sha256=PbQtJt9fSwgm5xeXgYWQih6eUSmJL_RwTxOa5T9SrZU,9391
|
||||
Cython/Compiler/Version.py,sha256=f2mS6aYYdu0DMRK3B4IuzMlCo-k-ffmehCao_vKlTdk,181
|
||||
Cython/Compiler/Visitor.pxd,sha256=KvOZgHoEREMTVYXr1ZoAk9H4n__rpmhIwE2S11ajeYM,1792
|
||||
Cython/Compiler/Visitor.py,sha256=iqtIsNaQwk8lSa6g_LnEU06CZtdnP7MDXbyodl4Ouwk,29984
|
||||
Cython/Compiler/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Compiler/__pycache__/AnalysedTreeTransforms.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Annotate.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/AutoDocTransforms.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Buffer.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Builtin.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/CmdLine.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Code.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/CodeGeneration.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/CythonScope.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/DebugFlags.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Errors.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/ExprNodes.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/FlowControl.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/FusedNode.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Future.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Interpreter.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Lexicon.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Main.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/MemoryView.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/ModuleNode.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Naming.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Nodes.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Optimize.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Options.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/ParseTreeTransforms.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Parsing.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Pipeline.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/PyrexTypes.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Pythran.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Scanning.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/StringEncoding.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Symtab.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/TreeFragment.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/TreePath.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/TypeInference.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/TypeSlots.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/UtilNodes.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/UtilityCode.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Version.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/Visitor.cpython-310.pyc,,
|
||||
Cython/Compiler/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Coverage.py,sha256=FtCMjKLYWvtULxWIzN-y3RhbwQwL4GtLyIjj-B3-07E,13537
|
||||
Cython/Debugger/Cygdb.py,sha256=CH_pXm0Jhl4SAe6sJXa5NS47vMmQ2KBbecyV56vLqFE,5751
|
||||
Cython/Debugger/DebugWriter.py,sha256=Yzz28JR4qZepxvxeu_1rJxIjJ4JbNQm5vM5e_UtNuRo,1945
|
||||
Cython/Debugger/Tests/TestLibCython.py,sha256=xrENLEahnp6WtOfokVtsALR6Ot2jFR6T5ZZRcaX0Vxk,8327
|
||||
Cython/Debugger/Tests/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Debugger/Tests/__pycache__/TestLibCython.cpython-310.pyc,,
|
||||
Cython/Debugger/Tests/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Debugger/Tests/__pycache__/test_libcython_in_gdb.cpython-310.pyc,,
|
||||
Cython/Debugger/Tests/__pycache__/test_libpython_in_gdb.cpython-310.pyc,,
|
||||
Cython/Debugger/Tests/cfuncs.c,sha256=4SZurmnz5J1SiIs9N26Eu4zc2wvF_qMEKaN0eTcbDPo,71
|
||||
Cython/Debugger/Tests/codefile,sha256=ugwpT9GPtYZIKe2Xco4PqikyA-poQAeYfE0icXmfb44,641
|
||||
Cython/Debugger/Tests/test_libcython_in_gdb.py,sha256=EvPTYkd7nzR3JtFim-ASLI6wfcYhgWfI4BQrucLJbHY,15804
|
||||
Cython/Debugger/Tests/test_libpython_in_gdb.py,sha256=1BD_FtMkmS4SoSQZq7MgAgDnvqIw3EcYZFVrtoCQmxo,4079
|
||||
Cython/Debugger/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Debugger/__pycache__/Cygdb.cpython-310.pyc,,
|
||||
Cython/Debugger/__pycache__/DebugWriter.cpython-310.pyc,,
|
||||
Cython/Debugger/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Debugger/__pycache__/libcython.cpython-310.pyc,,
|
||||
Cython/Debugger/__pycache__/libpython.cpython-310.pyc,,
|
||||
Cython/Debugger/libcython.py,sha256=Qs0qGzeUyeY___3jRDy_WWIDFGfSRH4al7On2XxkuNg,44949
|
||||
Cython/Debugger/libpython.py,sha256=IyTEdtGLnpQmt2XPgZ7oskQ8qGWWV2_5TMgZ5NhTA0k,90489
|
||||
Cython/Debugging.py,sha256=vFtJhn7QstMf5gnYru2qHIz5ZjPg1KSlZVGHr-pBCwM,552
|
||||
Cython/Distutils/__init__.py,sha256=uyWaN2NJ_mKYLzVsDPi0qZCdIYoW5M_7YYEmAOIL3Ek,98
|
||||
Cython/Distutils/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Distutils/__pycache__/build_ext.cpython-310.pyc,,
|
||||
Cython/Distutils/__pycache__/extension.cpython-310.pyc,,
|
||||
Cython/Distutils/__pycache__/old_build_ext.cpython-310.pyc,,
|
||||
Cython/Distutils/build_ext.py,sha256=Fc_cI5wN0fT1Mf2k5B5nH-PgZ8Gq2lL6OlzF_qzy3dA,1007
|
||||
Cython/Distutils/extension.py,sha256=FHvtK3Tj9MqE17TuZ_jWg1Mh4X7e-CXIPUpJK7nqcQE,4706
|
||||
Cython/Distutils/old_build_ext.py,sha256=Hy34A1HqhoDOyU-krN2gJUYXK2mYWc8E2EZB-stvmrE,13635
|
||||
Cython/Includes/Deprecated/python.pxd,sha256=l7crg8H9cVRedMcjDf_9xDLdnUT57Vt1BxlZWom-h88,61
|
||||
Cython/Includes/Deprecated/python_bool.pxd,sha256=qOaFbsP6_pKoB3HGTjQUkFhQqukXheCmcSnnBQLdKGQ,66
|
||||
Cython/Includes/Deprecated/python_buffer.pxd,sha256=gv2a3ngcOnRKZZHSox_bW1WD8jGbxfH9NJm1-iUXf9U,68
|
||||
Cython/Includes/Deprecated/python_bytes.pxd,sha256=07-Hk3YpN_i4mIlbWYbNgDkjEytQAYOepJLJTY1CrVk,67
|
||||
Cython/Includes/Deprecated/python_cobject.pxd,sha256=V9F0DHQbFZPbJ8RRnN9mft2ipq4wubM8ghBCGHr6NwE,69
|
||||
Cython/Includes/Deprecated/python_complex.pxd,sha256=ITmq55v0b1gibEpLSCTCz68ViljenSuGGjiWn_nvIvI,69
|
||||
Cython/Includes/Deprecated/python_dict.pxd,sha256=gYhGkJhMmzWcrXoPnJHUcp-vdtcwUACbGlfv3wtGsKU,66
|
||||
Cython/Includes/Deprecated/python_exc.pxd,sha256=irWdwDYRWU16-P54uGDNfUSUtkL5Sj_1zBDWext_80g,65
|
||||
Cython/Includes/Deprecated/python_float.pxd,sha256=v1Hbpd4SF3hSF7ZL_olMaYJzmBNA9jWn0eO9ggLBlvc,67
|
||||
Cython/Includes/Deprecated/python_function.pxd,sha256=lkYKySQy1W36hfyyAJsc3E-8d9bsx5k8OhIMFQ6k2jA,70
|
||||
Cython/Includes/Deprecated/python_getargs.pxd,sha256=NEdeqPqu4di0YJm_7yLfvuS903CAe4K2Pzb13TRfBdE,69
|
||||
Cython/Includes/Deprecated/python_instance.pxd,sha256=FX9UlYrSxDrzch7wUvh_Y5Ix-bsDYARkXzZJOg2FvEI,70
|
||||
Cython/Includes/Deprecated/python_int.pxd,sha256=Cwd4J4KTKjxwEMz1BbCso0g0pOID9AnySKOC1g0kLqA,65
|
||||
Cython/Includes/Deprecated/python_iterator.pxd,sha256=nPJ0nKSmnUVzI1SPrTSt9wSD7SQILyhONJdP0H_-FGc,70
|
||||
Cython/Includes/Deprecated/python_list.pxd,sha256=VHpylsg46-5Ud8rwlPe63bb3zSToXm9R_fPorZrJsUE,66
|
||||
Cython/Includes/Deprecated/python_long.pxd,sha256=pg8hOKNoKaW-Mslugzeq6NCeznJw939LT24AVQn_cqE,66
|
||||
Cython/Includes/Deprecated/python_mapping.pxd,sha256=AZtJdYm37glDSNChduAsgavz-_DPDkxxQEAO9lDGy84,69
|
||||
Cython/Includes/Deprecated/python_mem.pxd,sha256=Mxidel5P4yuJxJOvoYr0PN1FD78oCOIJUEMPYMYU7lE,65
|
||||
Cython/Includes/Deprecated/python_method.pxd,sha256=x5ye5_8KqtsW2HrEon5NdFJmIkmVDV1KeVpFsuC2UZE,68
|
||||
Cython/Includes/Deprecated/python_module.pxd,sha256=lKu5VYCgC6S7LSgFa22V2YTY9JfML0vABDZpChhxs60,68
|
||||
Cython/Includes/Deprecated/python_number.pxd,sha256=X4MxGoITZuJNPtC2cFJ8lQwui8MOC6rQfEDbFIcWA9k,68
|
||||
Cython/Includes/Deprecated/python_object.pxd,sha256=qr2OwYVot4ELK3_-mCfaktXgLJEaKWDyCEblQ2vXV-E,68
|
||||
Cython/Includes/Deprecated/python_oldbuffer.pxd,sha256=QyY4Vn5-cFaOt0oZ27GuRXa3tLawgMZN8KMamn9F1yo,71
|
||||
Cython/Includes/Deprecated/python_pycapsule.pxd,sha256=tHJfhgm1TrSwJQwQFdhwP7YE7oQFiegxhNhgCDmlB6A,71
|
||||
Cython/Includes/Deprecated/python_ref.pxd,sha256=wv39G35V7tN5sIhcL1APpe5NuhCwYwVy6X5DPPm5g5A,65
|
||||
Cython/Includes/Deprecated/python_sequence.pxd,sha256=9ycCua1ODfECKPd56_GBmeqzWrfdqmkjhbEmdt87NC0,70
|
||||
Cython/Includes/Deprecated/python_set.pxd,sha256=_Z5KVXs0V_T8fpgLX-2LbDAZIY1HnuhO-eTUHHRYwu0,65
|
||||
Cython/Includes/Deprecated/python_string.pxd,sha256=6VgAehwW9PcUC9Kp_HbRVMYPeF_Q-L8yr9o2ezuTzys,68
|
||||
Cython/Includes/Deprecated/python_tuple.pxd,sha256=_ZTQh7dRBmrRs9mtmOFjP37d0IFItxs20kzFtKtkY-g,67
|
||||
Cython/Includes/Deprecated/python_type.pxd,sha256=2OKmEdSqoyK8fXttlHG3NRguZ-ZikUUet-kjKLq-eEU,66
|
||||
Cython/Includes/Deprecated/python_unicode.pxd,sha256=TF8-N0un1WdyccTDo9hZVABc53SYzKnC3MEKrGb3vV0,69
|
||||
Cython/Includes/Deprecated/python_version.pxd,sha256=ZXrK0UGUt8vHbYPxm7PTdhMe1_h7Yj6Lo74oFxjnNns,69
|
||||
Cython/Includes/Deprecated/python_weakref.pxd,sha256=CUWMSmClrWPoTnlClOFCSHa6Xd55qDgIlcDCD6tfEhM,69
|
||||
Cython/Includes/Deprecated/stdio.pxd,sha256=lNc2YuvWJ-LNSSdN7adDo1lf-C2M0r10hH4bysha9Sg,64
|
||||
Cython/Includes/Deprecated/stdlib.pxd,sha256=PbCbjT8MjDjVRjx5Rod79gi22-9YI35jTulePAKCPXE,65
|
||||
Cython/Includes/Deprecated/stl.pxd,sha256=tHpByeYgNiclr3YtCdKKAeEs3CHJflqacC7YgV7YN8k,2187
|
||||
Cython/Includes/cpython/__init__.pxd,sha256=8URNRvb7JkYhqDZv2J0bVsdeZBEJBu7u2QFYkDyXPG8,8254
|
||||
Cython/Includes/cpython/array.pxd,sha256=g6apBiXJG_7a0mjGqkFaqlcQjsg64uKK1VlXFFyXVCk,6056
|
||||
Cython/Includes/cpython/bool.pxd,sha256=FaNn8K-Toq8FAws8BguKMk0IPM7IJm9IiUUGARSrKYk,1359
|
||||
Cython/Includes/cpython/buffer.pxd,sha256=wm7aHygGUof_H3-JyICOek_xiU6Oks178ark1Nfk-a0,4870
|
||||
Cython/Includes/cpython/bytearray.pxd,sha256=m0VdoHgouF1T0VtRjFLXZ5fi22vaMdVwFWpF3IxB6m4,1443
|
||||
Cython/Includes/cpython/bytes.pxd,sha256=tGLuiBMzQjurK_pq77CM7P0C-Hn0KUIDZCXW9QvlJAI,9906
|
||||
Cython/Includes/cpython/cellobject.pxd,sha256=DXdTjSN1RP1m4CsaGuggyIA1nGiIO4Kr7-c0ZWfrpRo,1390
|
||||
Cython/Includes/cpython/ceval.pxd,sha256=h6fBetZCUvWTcCn3bkXZg2kqnIuyC5ZSChyhOocxVus,236
|
||||
Cython/Includes/cpython/cobject.pxd,sha256=ZeMdbpZLqpcTywdv2VoppMTWD4X_yghL6Qox7LVfOyg,1524
|
||||
Cython/Includes/cpython/codecs.pxd,sha256=3fyudEljkNGQ7e3dJPst6udXGcAeNKvlMK9U8EB1gXc,5084
|
||||
Cython/Includes/cpython/complex.pxd,sha256=-bu0Cq91tS_U5tTra18S0jqt1FgSJTHXJ5J8rk-MOAA,1777
|
||||
Cython/Includes/cpython/conversion.pxd,sha256=dbbFuZJF0SscmcaNCUf0tlBQDRdKYf5tH8yzhTU_XYI,1696
|
||||
Cython/Includes/cpython/datetime.pxd,sha256=wQqB8i3tMZOTw9qrLdbHJRkxgZqscGEqmq0tIDfkkqw,6776
|
||||
Cython/Includes/cpython/dict.pxd,sha256=F-mrlcAfNmTSUkpJed63bp1IaO0cwG56t_DLk7f0xv0,6877
|
||||
Cython/Includes/cpython/exc.pxd,sha256=29-bGESwfoMqx1XU3MMggkIr8pz_l0UPruzy6KIzHxg,13606
|
||||
Cython/Includes/cpython/float.pxd,sha256=RD1qEAUocXG9qXrRiT8aCSSfGEyTzjTc9HQkv5xg1ZE,1424
|
||||
Cython/Includes/cpython/function.pxd,sha256=IoJUprbz8F10DEKh-vSSpY6nWkCHw7SqG9p2f-4gHek,2671
|
||||
Cython/Includes/cpython/genobject.pxd,sha256=emC1JPgkuvBbGC0rgeZapKDaXYEj48uWiDC-xF0Mx2I,1052
|
||||
Cython/Includes/cpython/getargs.pxd,sha256=268twKzdiAkQMXMsetNiNlNqaqzlKtiBENKbhOHd8x4,775
|
||||
Cython/Includes/cpython/instance.pxd,sha256=qCbxPeHKOJbuszDu3UEaI-KLX9lTopuaNCcpoHJ9ngU,985
|
||||
Cython/Includes/cpython/int.pxd,sha256=d9a0zUw_M3pRycCESWIjtfXWRvdvFOWxjdOjkcbX2gs,4131
|
||||
Cython/Includes/cpython/iterator.pxd,sha256=o52mLHbdm14Kqant2hR2zAdYzqK4fkSWZtBcRmpoP-I,1319
|
||||
Cython/Includes/cpython/iterobject.pxd,sha256=5UEZZwG5zyzxoCpknoQuh91zPUV11Uxr6F1taJdTv8k,1036
|
||||
Cython/Includes/cpython/list.pxd,sha256=t-xo7ROcewe6-0ztrNjsxMKV2KxD-ILUzemQ2tTuI7E,4084
|
||||
Cython/Includes/cpython/long.pxd,sha256=d6jHN1XJj7WL5PPAUK8U93IPyjWtlTmyhrBEVmxmGF8,7051
|
||||
Cython/Includes/cpython/longintrepr.pxd,sha256=czvKr3fQdYIwIRu3gojXssT9LFXH-nstM7f_lPt7lE4,480
|
||||
Cython/Includes/cpython/mapping.pxd,sha256=OIdvNVUoIpVCSQnkbLceTicSN0D_jRw6wQmbtxtxKuQ,2693
|
||||
Cython/Includes/cpython/mem.pxd,sha256=AWVinanXFBZXvU141we2dD8dkOqMJ8W3KAAzpBJqB5g,5386
|
||||
Cython/Includes/cpython/memoryview.pxd,sha256=l97J5-hbH3hp9aMbdXp3n73hJFNNsng6uyh40pc8P7I,2504
|
||||
Cython/Includes/cpython/method.pxd,sha256=UWXflhIlP4y7B5XDbH9rQ15iADciGW-iqV1-dlw2Wwg,2196
|
||||
Cython/Includes/cpython/module.pxd,sha256=Vc0Up7q1Mir38bN293E8RMugxWfuzjLFHM4g2dviPBM,9226
|
||||
Cython/Includes/cpython/number.pxd,sha256=tYJ0nn0k_llUx3ilniW9iXd2rKVejA-J5UUiIJ36Kww,11922
|
||||
Cython/Includes/cpython/object.pxd,sha256=AK5D-LrDbvisO6wpkh29G6xjA71sBF_KfKUyn0k2hzg,18366
|
||||
Cython/Includes/cpython/oldbuffer.pxd,sha256=v0-YZ_Iwwj3ZQdM8VE5NPTQcbBlJdWwJGtNO9DonGgw,2916
|
||||
Cython/Includes/cpython/pycapsule.pxd,sha256=8vySKea_zyTZZ1H39lICYkpJmnYTqZrun3DLf7d2294,5692
|
||||
Cython/Includes/cpython/pylifecycle.pxd,sha256=LziJZHclGdtsr3yT28fULHNZ_n67bs1DmI9s8YzrBGg,2000
|
||||
Cython/Includes/cpython/pystate.pxd,sha256=xgf1BBkv36qvqMaR77zZWYOuonAwe4RfNKE2g91A6fk,3683
|
||||
Cython/Includes/cpython/pythread.pxd,sha256=0375TaYmtNCDDkWBh9WY4oJ_jhoTxhu_RR5QiOsXmYg,1946
|
||||
Cython/Includes/cpython/ref.pxd,sha256=2AmgyGDhwA4scts0jcBTdGTCG0b2P8-eYAKFJk44x0I,2557
|
||||
Cython/Includes/cpython/sequence.pxd,sha256=iTp3C6wOvTdvjLmdj3k9GqQqCGDlQFpzWi07wVQqSS4,6008
|
||||
Cython/Includes/cpython/set.pxd,sha256=ewHRPVMbHUGDInZ3NziisCq68LvtmEJ-SXFbzmuJxLc,5383
|
||||
Cython/Includes/cpython/slice.pxd,sha256=Rzgn8diAsN7lS2xGTq4VZucV3ziFNra4oz4tKGEAkMo,3111
|
||||
Cython/Includes/cpython/string.pxd,sha256=EKjDGFnPcjnkndwGMJqRrszDV390Mc6o7AADChnNCiA,9944
|
||||
Cython/Includes/cpython/tuple.pxd,sha256=eOLfH75ftJeYszztGFWWZP7LnyFOgw8GNuE7PQ9hAvs,3206
|
||||
Cython/Includes/cpython/type.pxd,sha256=FOypwX0ZYamPc4uO8bejzO-HzgiaSRaXEPsxxxPIneI,1831
|
||||
Cython/Includes/cpython/unicode.pxd,sha256=BYaF26EbiStNE62WHjLFFxAygoU9zwSezQDOpjxHse8,26242
|
||||
Cython/Includes/cpython/version.pxd,sha256=l5KXt04isEv3qbGRJZ8fNlCYGO24HsA2l4EM3RxTEhE,847
|
||||
Cython/Includes/cpython/weakref.pxd,sha256=UU9H_ovHG07FFgP_kY2xhGv3yJDr_8iujCZnxH2jnlo,1984
|
||||
Cython/Includes/libc/__init__.pxd,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Includes/libc/errno.pxd,sha256=j5hcKx7zinivU2b6SFMy8LZ9sJIQY5XLrp9cQUKv5AQ,2050
|
||||
Cython/Includes/libc/float.pxd,sha256=IhvZJljpTG0fZtcIp7EBO2Sqddozxoxwj4RFNVcKLpY,966
|
||||
Cython/Includes/libc/limits.pxd,sha256=xHlIyuDIKpjqclvRRYzZIcfd5G1re5QtbmoDMqZR_Ec,621
|
||||
Cython/Includes/libc/locale.pxd,sha256=sixG8EJ6wiVb0HIR1LWJ3lXTjTv463GJ9C_40HRovN4,1140
|
||||
Cython/Includes/libc/math.pxd,sha256=51YUxSe01R96_rr3sj4n4MLW-eOmQbcwdNn8YthTxqg,2948
|
||||
Cython/Includes/libc/setjmp.pxd,sha256=XRh-gSuhvFLl0nRvz5OhSWYe9eqX2attAck3JI7mwa4,297
|
||||
Cython/Includes/libc/signal.pxd,sha256=XOScPDA5vzlfEmu4D7DFT1-5Eu3qMpYdUarjt-fqlbw,1170
|
||||
Cython/Includes/libc/stddef.pxd,sha256=0rCyoocCfDL-1OQo3pxHQ-6fW20SAYktOLPoa4d97w8,164
|
||||
Cython/Includes/libc/stdint.pxd,sha256=qHJXzpWCrbvJWSaHYZL27VJPupQreTZl9VGj0jgLdRU,3449
|
||||
Cython/Includes/libc/stdio.pxd,sha256=qUaxEwNrQl1-4yHLorzzJZ-a-y5_-Rm_m7Z5meaRqH0,2476
|
||||
Cython/Includes/libc/stdlib.pxd,sha256=p62xq2XfB24WfNCjRXgD6cOYoRuV47AnYijkjWv4ugE,2444
|
||||
Cython/Includes/libc/string.pxd,sha256=tzYGbRrnccedFLes-KGgJqM0FEtwHF_q4f2fqltNvyE,2038
|
||||
Cython/Includes/libc/time.pxd,sha256=-IRH7fTq3wKBKmQQnpZRhaLsnl7D_qXFz_4BLB9O3u0,1317
|
||||
Cython/Includes/libcpp/__init__.pxd,sha256=PCx8ZRfOeoyMRu41PPlPY9uo2kZmt_7d0KR4Epzfe7c,94
|
||||
Cython/Includes/libcpp/algorithm.pxd,sha256=-2V0oR_cFbHHzeWT9RcfLvi5Oy-s_V2lO3OI6ZtX6fM,1770
|
||||
Cython/Includes/libcpp/cast.pxd,sha256=En4LBubdinfpm9Rel077tK_LGwg_3k4FAu9mlIbKjuw,501
|
||||
Cython/Includes/libcpp/complex.pxd,sha256=IjL8y9sAglhGbTKhqsJbW0mgMTYEUbYM1ekr5VDhQgY,3012
|
||||
Cython/Includes/libcpp/deque.pxd,sha256=aWqZ9j3OgQuqFLkqRO_U2FIwbSe2fKmmYDRAfD0vGqU,3106
|
||||
Cython/Includes/libcpp/forward_list.pxd,sha256=-So1ExEOkoPfsSdMlJSlI5665-zyWLMoUxlmm2Dlokk,2392
|
||||
Cython/Includes/libcpp/functional.pxd,sha256=BXPYkffEOlKO1erTLqlkBLex6Gb5byDMF4hq_MZ2aVI,381
|
||||
Cython/Includes/libcpp/iterator.pxd,sha256=mVc1rsAYfn_ARrdQ4JG-Ut5il5ynIa1CRXLk8Be8Zks,1432
|
||||
Cython/Includes/libcpp/limits.pxd,sha256=RKV3wPvk4tV_vX5CYQRJIK5m5xXav7SeBxltlLyk8es,1661
|
||||
Cython/Includes/libcpp/list.pxd,sha256=rGQfB3_mDcRkGKtMBuvDQvAPmgzR5jxSf3eOSRgR4YA,2658
|
||||
Cython/Includes/libcpp/map.pxd,sha256=GF2sDnFBHZoU3Rcuo1rn6yKh45nhkX0_iH29xj581ow,2551
|
||||
Cython/Includes/libcpp/memory.pxd,sha256=Hj20aSnmUTPAhFCrlmF_aeHJKiMiZ2bDKhaYn2yybJo,3600
|
||||
Cython/Includes/libcpp/pair.pxd,sha256=UBJXw43uHkDlNsr0Pu1aP5tZ-ILXhUAyOLam2qdWmZA,27
|
||||
Cython/Includes/libcpp/queue.pxd,sha256=FbL4Q7C3lgtZ2YzictU1XBXzQ7G-6y9i_7l2eqzA3Xc,649
|
||||
Cython/Includes/libcpp/set.pxd,sha256=3y5Ir2TjGD7g3VRvlkXV1a3V3ZYzJvwOAfeTv8ucOCw,2170
|
||||
Cython/Includes/libcpp/stack.pxd,sha256=zM3SQOqMWONVqud13ag3bUupA-ozU_YMq4Ad2QkL6fI,292
|
||||
Cython/Includes/libcpp/string.pxd,sha256=zsvzyW6IggIDFqcF-UuLjxiNAHPtToAoc9VhKKG2U5A,8731
|
||||
Cython/Includes/libcpp/typeindex.pxd,sha256=mIHr5Mq6Lol0SlzqeK6w_giVERh3uAjZm78yPDLXzc4,524
|
||||
Cython/Includes/libcpp/typeinfo.pxd,sha256=tITsqurrdaZjsEGFksem9xZtVhSxQRxHZxcoC-4Y-DY,304
|
||||
Cython/Includes/libcpp/unordered_map.pxd,sha256=eNna4hRAucQLnliBfEMu7Unfd_lB18I42iwKmsCO0-M,2867
|
||||
Cython/Includes/libcpp/unordered_set.pxd,sha256=eUYSOMT5Gt8kZWCUKezQGyXWzatEyNg6-nmAlmcBo-k,2622
|
||||
Cython/Includes/libcpp/utility.pxd,sha256=hTbvp7c12pnU2yvzzMvflZB-MAc_--3xh3PXtD_VIwg,1040
|
||||
Cython/Includes/libcpp/vector.pxd,sha256=GYqLb74owhMmNQHUCcZSxGcYPgNuw6qULsfWKr7g6OQ,3350
|
||||
Cython/Includes/numpy/__init__.pxd,sha256=CbLwvA4u-xj7RHxbO9Hs2o6hXd7GaJJlGEn9XJVH4c4,38138
|
||||
Cython/Includes/numpy/math.pxd,sha256=qZEdamaPgCFW4J7Itc6BWgOrQSKZdxDT6kbU_gqx2g4,5807
|
||||
Cython/Includes/openmp.pxd,sha256=orCIBYFuVPtLdRdhhCm5uhGbeV_fgVCA2Jk2Bts1e2g,1713
|
||||
Cython/Includes/posix/__init__.pxd,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Includes/posix/dlfcn.pxd,sha256=2IFcGBfZEmArdE0BxB71eT_Yb7n9STaVM11AtUcg_pE,355
|
||||
Cython/Includes/posix/fcntl.pxd,sha256=oRL8-OsgcplHMGcYq5-W5twISvxK-vDfzAaEfuQHC-4,1194
|
||||
Cython/Includes/posix/ioctl.pxd,sha256=2RC5zejPOCTkarDZM_6Vd2wc4oBuN7iaiL_C5MPBs90,99
|
||||
Cython/Includes/posix/mman.pxd,sha256=juJcLi92N9Bc6L2p4zrUmYQIgNmrTsZ6hExbl1181pc,3362
|
||||
Cython/Includes/posix/resource.pxd,sha256=MQe1bCTYQFVMsago3pgOvR6t6NElQElg7rhVANxYRcE,1254
|
||||
Cython/Includes/posix/select.pxd,sha256=e4nhGHR8TRw6Xs9du5JoFtkd8U9sm3gX_BHq2FfmU6E,546
|
||||
Cython/Includes/posix/signal.pxd,sha256=wFJI5UthdtU9mZWjEBeZ9IIfeX252JVwDk2tsbW_q3U,1876
|
||||
Cython/Includes/posix/stat.pxd,sha256=ZOcPCpXnxlRRHcUkvg559hrFfB75uTbIYRWoQeyBCYs,1734
|
||||
Cython/Includes/posix/stdio.pxd,sha256=K8DEH38hWMvy2A8zcKbHRrHSGsgwTIrQ9qCzU-0cWS0,1054
|
||||
Cython/Includes/posix/stdlib.pxd,sha256=uGRPa00_HWZ6Chv5E13F96eut0xWHSfR7IioK9rKVLY,934
|
||||
Cython/Includes/posix/strings.pxd,sha256=GNEteqND2wgXXSvkv6U9eKSC9oIom3C7o2zQ6W_J_S4,374
|
||||
Cython/Includes/posix/time.pxd,sha256=wPUD7AjxpxmnUYmogTMFjroB2VzcPh8-b_8NEj-yG14,1980
|
||||
Cython/Includes/posix/types.pxd,sha256=tWEWxST4EGHIgYS-Ce2SGjZ-KgmM2SVe1eggdcgv3JQ,1162
|
||||
Cython/Includes/posix/unistd.pxd,sha256=w9B4d9NaXBsQ62XOr2xe9UFPGewmEk5BG6sqiRWdoM8,8061
|
||||
Cython/Includes/posix/wait.pxd,sha256=WNogQvKu2hMfEQiCyaANfVWFnyJSk6TxBU0c6npeJrA,1244
|
||||
Cython/Plex/Actions.pxd,sha256=FC-6ffzWR4i3rR6VSL2C64Xxs1qBhpBEzRsU7WpLn1Y,585
|
||||
Cython/Plex/Actions.py,sha256=Caxkx8Kup9m4sx24ZcDTq-fAfPGG06TAHu2NI1D9zPs,2545
|
||||
Cython/Plex/DFA.py,sha256=w4vl2ejXv6ptILtkTCbB8NcvF8ylwc6DaQ2gPFrWuo4,6012
|
||||
Cython/Plex/Errors.py,sha256=As5uuGmqZe4w0B7Dm981lZTnDG-nlXSHYqiGUKnhrrY,1169
|
||||
Cython/Plex/Lexicons.py,sha256=ay3yy9fqI5y5lfgpJ4ubBjYZQ53gFDVgNGbmoSl5DxI,6907
|
||||
Cython/Plex/Machines.py,sha256=bIKg3-yxD_r7x-zEowJ7EsPBWlgXm_XhIozqsLQBeTk,7760
|
||||
Cython/Plex/Regexps.py,sha256=qaP-Fr-GgKNmBVsMyXO3ltl2HH1JQcQiFmX2oyUyeOA,16208
|
||||
Cython/Plex/Scanners.pxd,sha256=oSfcDUZ3syc2ag73udwU5xoaIGDxiNd8a2F_LLw5PzY,1481
|
||||
Cython/Plex/Scanners.py,sha256=-TXAxKW43ZbQNCSEkMWEJ0SmqYVVCkSOT9UngOCRZnQ,12259
|
||||
Cython/Plex/Timing.py,sha256=-VgQveS-Ip_2ErjrVxh4w7cXpyVBkUaSaiLadyD3bw0,472
|
||||
Cython/Plex/Traditional.py,sha256=cAT-pZnqIwCJaqgSqgKODSznFZ5DunUw_MLWx8Y650c,4120
|
||||
Cython/Plex/Transitions.py,sha256=Tvp7cFXR3ZBPPHm0TAhUMC_-uiRR9YdOkF4t0wtk-f0,7187
|
||||
Cython/Plex/__init__.py,sha256=dvMeQpSyZE75W0gkf4Xo5LAxgQLNhkAXiQoIOtcOkZ0,1282
|
||||
Cython/Plex/__pycache__/Actions.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/DFA.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Errors.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Lexicons.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Machines.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Regexps.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Scanners.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Timing.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Traditional.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/Transitions.cpython-310.pyc,,
|
||||
Cython/Plex/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Runtime/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Runtime/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Runtime/refnanny.pyx,sha256=f2p1_0YxK25lm8Qfsu2ytvl0Im7GYyix1Q9krEBwC6c,6279
|
||||
Cython/Shadow.py,sha256=UZbJn_G5qqi8UJjVurS93b9H91PKrcbdNKXawkTIjWQ,13007
|
||||
Cython/StringIOTree.py,sha256=GX-TWn9XHwY5ecb4in8ovsTS5CtPTsSxZpanLWmQxgE,3336
|
||||
Cython/Tempita/__init__.py,sha256=YHujYHiLoYUwFNNswJCgzSrDuie3sV08JsWT9Nbmp78,152
|
||||
Cython/Tempita/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Tempita/__pycache__/_looper.cpython-310.pyc,,
|
||||
Cython/Tempita/__pycache__/_tempita.cpython-310.pyc,,
|
||||
Cython/Tempita/__pycache__/compat3.cpython-310.pyc,,
|
||||
Cython/Tempita/_looper.py,sha256=jlStYhz9Pgp6NatX86k-netBNBmvwaeWxCRS_S8vcIM,4168
|
||||
Cython/Tempita/_tempita.py,sha256=4gnJhuVIsGciu_5Besbvw26g82Pm7CiXazMghZO3ejs,39588
|
||||
Cython/Tempita/compat3.py,sha256=cjW1y266vRF5Xvh8kAu7_qHGT8AGGu2kGSJRK6DI-0E,903
|
||||
Cython/TestUtils.py,sha256=fzpic9xU-LP0wempXqwUQWZapBvXnFgbW_W9--IKpIA,7979
|
||||
Cython/Tests/TestCodeWriter.py,sha256=qKad43J3hN7PLp7mVbEDESt96qsk8y3ELRwwIp9jnNw,2316
|
||||
Cython/Tests/TestCythonUtils.py,sha256=XF4Fw4J5HZ4jUPLVv7ea8ZZcl2i9yXn5cx27WTtrcmU,474
|
||||
Cython/Tests/TestJediTyper.py,sha256=F6MUG8SdzGXQwkbw6Wv1PqVlmlIT1z_7lH2buVOFT_I,6996
|
||||
Cython/Tests/TestStringIOTree.py,sha256=vTuu3z32WTcmJaf0fBq62NMghYtaPL2rRnfdl2WM--4,1946
|
||||
Cython/Tests/__init__.py,sha256=jOqtmPLCvMCq0xVMwGekuLpBmVgq0xtPFmUePySdOjs,13
|
||||
Cython/Tests/__pycache__/TestCodeWriter.cpython-310.pyc,,
|
||||
Cython/Tests/__pycache__/TestCythonUtils.cpython-310.pyc,,
|
||||
Cython/Tests/__pycache__/TestJediTyper.cpython-310.pyc,,
|
||||
Cython/Tests/__pycache__/TestStringIOTree.cpython-310.pyc,,
|
||||
Cython/Tests/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Tests/__pycache__/xmlrunner.cpython-310.pyc,,
|
||||
Cython/Tests/xmlrunner.py,sha256=N1Z_C4Q_rSWFNQxm3L99qX-SaIWEksXbmfXOd_srg3s,14801
|
||||
Cython/Utility/AsyncGen.c,sha256=iwNH8NdegHZYafEgBpErk6erU5o6GKVtHgqd3Vq9kNc,41023
|
||||
Cython/Utility/Buffer.c,sha256=VUF4xHKJGX7QMTvpJO40aI1JUL-SERLEvlXXXEk2dHU,29654
|
||||
Cython/Utility/Builtins.c,sha256=gYObNoiK_NVWRuzSFRePMb-dtw-XCp_Dx_Ztjmpq7as,16818
|
||||
Cython/Utility/CConvert.pyx,sha256=fbZVHvm2vlWj2rgm8ajBt5jrbN30nY7dEmHlBCGomlU,4338
|
||||
Cython/Utility/CMath.c,sha256=GIc7gd2WzaZryDJM3tefqXifLJpUJs6_T_c_mFrr-s8,2566
|
||||
Cython/Utility/Capsule.c,sha256=SOeU7E7T7piQEx894T2QFH2RlSG-MmsiyuY4lVN1yso,505
|
||||
Cython/Utility/CommonStructures.c,sha256=p65HHgTrf7h7Tj7JK7tIgkLrrCrjouL8HL90EHfoMoU,2558
|
||||
Cython/Utility/Complex.c,sha256=J4HseVcBOzGo5dye0Gus8bf8rGwWLEzN9sRJM74SWiI,10043
|
||||
Cython/Utility/Coroutine.c,sha256=YmFtN3eY_MAxEktax_xwsdPhL8qUC86jUgPe1mdlcdU,89569
|
||||
Cython/Utility/CpdefEnums.pyx,sha256=XMg8sdltQSNj2wGVfnHIWRvyHFCcLK8ZfpKznKi4lhY,1893
|
||||
Cython/Utility/CppConvert.pyx,sha256=-e5i3_J1SS_GbctsflQwylx9cqdk_CJ2SfQSEDHa71k,6098
|
||||
Cython/Utility/CppSupport.cpp,sha256=NTnSRCmi2PHuT3J6Qy15xMZGx0bf9l-MaxAbW6OVk6s,2234
|
||||
Cython/Utility/CythonFunction.c,sha256=m_K3U-13w4dbg_Q3Dy4Webf75Ftj9oz-rahk3OekO84,46154
|
||||
Cython/Utility/Embed.c,sha256=sMDv7XVJswaGRTQbQHtEDThZaAmvbn-6yeIqrUzboL4,6854
|
||||
Cython/Utility/Exceptions.c,sha256=mXEVREHj4zA9a0oY2Dbw1VOXtDhsL2wBSCMUS3ZFhSI,27314
|
||||
Cython/Utility/ExtensionTypes.c,sha256=doPkCDEsyClYfDa1386DxwVj0D4jYvKQlnbXQo2REHM,11664
|
||||
Cython/Utility/FunctionArguments.c,sha256=IH9Y5aV_tNrJLo_CWHskEnFai9fp9cKLvRkIZYl2UGQ,12040
|
||||
Cython/Utility/ImportExport.c,sha256=hs-AMn_uWVhQwQQds2PG_szRSe8hZIm8heMh1-XMQzc,21760
|
||||
Cython/Utility/MemoryView.pyx,sha256=5WnL5DJF0nuSQBN5oBeEHFYE8aYm4C0-hY9Azm-toY4,49749
|
||||
Cython/Utility/MemoryView_C.c,sha256=wlIKZ6UxkH--BFA-ENr83FtUw0KtStkqjAvB24Q0y_s,29072
|
||||
Cython/Utility/ModuleSetupCode.c,sha256=bTsiPDYlC-azcN_T7mAn3mItgBV-CLPzKiEpSWLH4XI,58489
|
||||
Cython/Utility/ObjectHandling.c,sha256=AcTgr9Kw0iiFqX8oDQ6m7NrOm7ogny1chNs1HhuEwNc,88765
|
||||
Cython/Utility/Optimize.c,sha256=xtKZ8WhOe8l4UYONGVDilNFhzGMueJxA7nYJn8QIWmc,45184
|
||||
Cython/Utility/Overflow.c,sha256=_KXlJsbMIi-jzdCotwxkN6mtqo6jHRNnPJ1ZKwXVhpE,12424
|
||||
Cython/Utility/Printing.c,sha256=o8XnfjNIT8Ub5KY4FAp_FNw-OE3xqjy0MgmYWgDcWao,5103
|
||||
Cython/Utility/Profile.c,sha256=3aq_eC7h_nUnZe-Np7td20ublCC4OowfHoV3FoW9UnU,17922
|
||||
Cython/Utility/StringTools.c,sha256=UyWngTrFElFXl4NIi7n36QCM7HL3MCNGhvqpPYLLY6o,42215
|
||||
Cython/Utility/TestCyUtilityLoader.pyx,sha256=91lWWJub7l_6xNn3ncrvQZZ94RpkQzEx2NtAaFpvrxY,152
|
||||
Cython/Utility/TestCythonScope.pyx,sha256=HQm5E5Eehr3tkDDURURyVnDputKG3-Wn2k2aIAoru9g,1595
|
||||
Cython/Utility/TestUtilityLoader.c,sha256=dGy6ZWL2kBqtmUY7kF75UEox5kadQZ__BmZKscwg2aY,279
|
||||
Cython/Utility/TypeConversion.c,sha256=0K3erVzNT9lY-jEygTxsarAirETGZcOzvSK-VDs5EJY,36302
|
||||
Cython/Utility/__init__.py,sha256=t2bpY-TYSX8lJdbKuBFJ1kBfpWVzgGw4xoZlCKfyj_s,1159
|
||||
Cython/Utility/__pycache__/__init__.cpython-310.pyc,,
|
||||
Cython/Utility/arrayarray.h,sha256=3Ll8Gd_S4rv8HaTfg5i6-aaoB9taI1vzwTp7NeA7Wy0,4089
|
||||
Cython/Utils.py,sha256=YJa4RP6fSQkWpbA_X0DnNREd3Y5NuPDjGwUdNvVW2OM,13458
|
||||
Cython/__init__.py,sha256=GMnkoIas6hfN_meqZAJF9BEs1NuY4-4B2L0Uls7hXaA,358
|
||||
Cython/__pycache__/CodeWriter.cpython-310.pyc,,
|
||||
Cython/__pycache__/Coverage.cpython-310.pyc,,
|
||||
Cython/__pycache__/Debugging.cpython-310.pyc,,
|
||||
Cython/__pycache__/Shadow.cpython-310.pyc,,
|
||||
Cython/__pycache__/StringIOTree.cpython-310.pyc,,
|
||||
Cython/__pycache__/TestUtils.cpython-310.pyc,,
|
||||
Cython/__pycache__/Utils.cpython-310.pyc,,
|
||||
Cython/__pycache__/__init__.cpython-310.pyc,,
|
||||
__pycache__/cython.cpython-310.pyc,,
|
||||
cython.py,sha256=z2AtgHBGh0x0h0ZcGje7IhYlR6nGH_MmOh1fFMjqYn0,520
|
||||
pyximport/__init__.py,sha256=9hOyKolFtOerPiVEyktKrT1VtzbGexq9UmORzo52iHI,79
|
||||
pyximport/__pycache__/__init__.cpython-310.pyc,,
|
||||
pyximport/__pycache__/pyxbuild.cpython-310.pyc,,
|
||||
pyximport/__pycache__/pyximport.cpython-310.pyc,,
|
||||
pyximport/pyxbuild.py,sha256=TiAkhtSxSbRW04JKtgO3FP3hfVzQ1mjjzCh5PqZDOrM,5702
|
||||
pyximport/pyximport.py,sha256=Vjxp3kbmFRf9j0ya4f0m0Ahytkjjmv2UkFueasXxL5A,23578
|
||||
@@ -0,0 +1,6 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.37.1)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[console_scripts]
|
||||
cygdb = Cython.Debugger.Cygdb:main
|
||||
cython = Cython.Compiler.Main:setuptools_main
|
||||
cythonize = Cython.Build.Cythonize:main
|
||||
@@ -0,0 +1,3 @@
|
||||
Cython
|
||||
cython
|
||||
pyximport
|
||||
@@ -0,0 +1,816 @@
|
||||
"""
|
||||
Serializes a Cython code tree to Cython code. This is primarily useful for
|
||||
debugging and testing purposes.
|
||||
|
||||
The output is in a strict format, no whitespace or comments from the input
|
||||
is preserved (and it could not be as it is not present in the code tree).
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from .Compiler.Visitor import TreeVisitor
|
||||
from .Compiler.ExprNodes import *
|
||||
|
||||
|
||||
class LinesResult(object):
|
||||
def __init__(self):
|
||||
self.lines = []
|
||||
self.s = u""
|
||||
|
||||
def put(self, s):
|
||||
self.s += s
|
||||
|
||||
def newline(self):
|
||||
self.lines.append(self.s)
|
||||
self.s = u""
|
||||
|
||||
def putline(self, s):
|
||||
self.put(s)
|
||||
self.newline()
|
||||
|
||||
class DeclarationWriter(TreeVisitor):
|
||||
|
||||
indent_string = u" "
|
||||
|
||||
def __init__(self, result=None):
|
||||
super(DeclarationWriter, self).__init__()
|
||||
if result is None:
|
||||
result = LinesResult()
|
||||
self.result = result
|
||||
self.numindents = 0
|
||||
self.tempnames = {}
|
||||
self.tempblockindex = 0
|
||||
|
||||
def write(self, tree):
|
||||
self.visit(tree)
|
||||
return self.result
|
||||
|
||||
def indent(self):
|
||||
self.numindents += 1
|
||||
|
||||
def dedent(self):
|
||||
self.numindents -= 1
|
||||
|
||||
def startline(self, s=u""):
|
||||
self.result.put(self.indent_string * self.numindents + s)
|
||||
|
||||
def put(self, s):
|
||||
self.result.put(s)
|
||||
|
||||
def putline(self, s):
|
||||
self.result.putline(self.indent_string * self.numindents + s)
|
||||
|
||||
def endline(self, s=u""):
|
||||
self.result.putline(s)
|
||||
|
||||
def line(self, s):
|
||||
self.startline(s)
|
||||
self.endline()
|
||||
|
||||
def comma_separated_list(self, items, output_rhs=False):
|
||||
if len(items) > 0:
|
||||
for item in items[:-1]:
|
||||
self.visit(item)
|
||||
if output_rhs and item.default is not None:
|
||||
self.put(u" = ")
|
||||
self.visit(item.default)
|
||||
self.put(u", ")
|
||||
self.visit(items[-1])
|
||||
|
||||
def visit_Node(self, node):
|
||||
raise AssertionError("Node not handled by serializer: %r" % node)
|
||||
|
||||
def visit_ModuleNode(self, node):
|
||||
self.visitchildren(node)
|
||||
|
||||
def visit_StatListNode(self, node):
|
||||
self.visitchildren(node)
|
||||
|
||||
def visit_CDefExternNode(self, node):
|
||||
if node.include_file is None:
|
||||
file = u'*'
|
||||
else:
|
||||
file = u'"%s"' % node.include_file
|
||||
self.putline(u"cdef extern from %s:" % file)
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
|
||||
def visit_CPtrDeclaratorNode(self, node):
|
||||
self.put('*')
|
||||
self.visit(node.base)
|
||||
|
||||
def visit_CReferenceDeclaratorNode(self, node):
|
||||
self.put('&')
|
||||
self.visit(node.base)
|
||||
|
||||
def visit_CArrayDeclaratorNode(self, node):
|
||||
self.visit(node.base)
|
||||
self.put(u'[')
|
||||
if node.dimension is not None:
|
||||
self.visit(node.dimension)
|
||||
self.put(u']')
|
||||
|
||||
def visit_CArrayDeclaratorNode(self, node):
|
||||
self.visit(node.base)
|
||||
self.put(u'[')
|
||||
if node.dimension is not None:
|
||||
self.visit(node.dimension)
|
||||
self.put(u']')
|
||||
|
||||
def visit_CFuncDeclaratorNode(self, node):
|
||||
# TODO: except, gil, etc.
|
||||
self.visit(node.base)
|
||||
self.put(u'(')
|
||||
self.comma_separated_list(node.args)
|
||||
self.endline(u')')
|
||||
|
||||
def visit_CNameDeclaratorNode(self, node):
|
||||
self.put(node.name)
|
||||
|
||||
def visit_CSimpleBaseTypeNode(self, node):
|
||||
# See Parsing.p_sign_and_longness
|
||||
if node.is_basic_c_type:
|
||||
self.put(("unsigned ", "", "signed ")[node.signed])
|
||||
if node.longness < 0:
|
||||
self.put("short " * -node.longness)
|
||||
elif node.longness > 0:
|
||||
self.put("long " * node.longness)
|
||||
self.put(node.name)
|
||||
|
||||
def visit_CComplexBaseTypeNode(self, node):
|
||||
self.put(u'(')
|
||||
self.visit(node.base_type)
|
||||
self.visit(node.declarator)
|
||||
self.put(u')')
|
||||
|
||||
def visit_CNestedBaseTypeNode(self, node):
|
||||
self.visit(node.base_type)
|
||||
self.put(u'.')
|
||||
self.put(node.name)
|
||||
|
||||
def visit_TemplatedTypeNode(self, node):
|
||||
self.visit(node.base_type_node)
|
||||
self.put(u'[')
|
||||
self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs)
|
||||
self.put(u']')
|
||||
|
||||
def visit_CVarDefNode(self, node):
|
||||
self.startline(u"cdef ")
|
||||
self.visit(node.base_type)
|
||||
self.put(u" ")
|
||||
self.comma_separated_list(node.declarators, output_rhs=True)
|
||||
self.endline()
|
||||
|
||||
def visit_container_node(self, node, decl, extras, attributes):
|
||||
# TODO: visibility
|
||||
self.startline(decl)
|
||||
if node.name:
|
||||
self.put(u' ')
|
||||
self.put(node.name)
|
||||
if node.cname is not None:
|
||||
self.put(u' "%s"' % node.cname)
|
||||
if extras:
|
||||
self.put(extras)
|
||||
self.endline(':')
|
||||
self.indent()
|
||||
if not attributes:
|
||||
self.putline('pass')
|
||||
else:
|
||||
for attribute in attributes:
|
||||
self.visit(attribute)
|
||||
self.dedent()
|
||||
|
||||
def visit_CStructOrUnionDefNode(self, node):
|
||||
if node.typedef_flag:
|
||||
decl = u'ctypedef '
|
||||
else:
|
||||
decl = u'cdef '
|
||||
if node.visibility == 'public':
|
||||
decl += u'public '
|
||||
if node.packed:
|
||||
decl += u'packed '
|
||||
decl += node.kind
|
||||
self.visit_container_node(node, decl, None, node.attributes)
|
||||
|
||||
def visit_CppClassNode(self, node):
|
||||
extras = ""
|
||||
if node.templates:
|
||||
extras = u"[%s]" % ", ".join(node.templates)
|
||||
if node.base_classes:
|
||||
extras += "(%s)" % ", ".join(node.base_classes)
|
||||
self.visit_container_node(node, u"cdef cppclass", extras, node.attributes)
|
||||
|
||||
def visit_CEnumDefNode(self, node):
|
||||
self.visit_container_node(node, u"cdef enum", None, node.items)
|
||||
|
||||
def visit_CEnumDefItemNode(self, node):
|
||||
self.startline(node.name)
|
||||
if node.cname:
|
||||
self.put(u' "%s"' % node.cname)
|
||||
if node.value:
|
||||
self.put(u" = ")
|
||||
self.visit(node.value)
|
||||
self.endline()
|
||||
|
||||
def visit_CClassDefNode(self, node):
|
||||
assert not node.module_name
|
||||
if node.decorators:
|
||||
for decorator in node.decorators:
|
||||
self.visit(decorator)
|
||||
self.startline(u"cdef class ")
|
||||
self.put(node.class_name)
|
||||
if node.base_class_name:
|
||||
self.put(u"(")
|
||||
if node.base_class_module:
|
||||
self.put(node.base_class_module)
|
||||
self.put(u".")
|
||||
self.put(node.base_class_name)
|
||||
self.put(u")")
|
||||
self.endline(u":")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
|
||||
def visit_CTypeDefNode(self, node):
|
||||
self.startline(u"ctypedef ")
|
||||
self.visit(node.base_type)
|
||||
self.put(u" ")
|
||||
self.visit(node.declarator)
|
||||
self.endline()
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
self.startline(u"def %s(" % node.name)
|
||||
self.comma_separated_list(node.args)
|
||||
self.endline(u"):")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
|
||||
def visit_CArgDeclNode(self, node):
|
||||
if node.base_type.name is not None:
|
||||
self.visit(node.base_type)
|
||||
self.put(u" ")
|
||||
self.visit(node.declarator)
|
||||
if node.default is not None:
|
||||
self.put(u" = ")
|
||||
self.visit(node.default)
|
||||
|
||||
def visit_CImportStatNode(self, node):
|
||||
self.startline(u"cimport ")
|
||||
self.put(node.module_name)
|
||||
if node.as_name:
|
||||
self.put(u" as ")
|
||||
self.put(node.as_name)
|
||||
self.endline()
|
||||
|
||||
def visit_FromCImportStatNode(self, node):
|
||||
self.startline(u"from ")
|
||||
self.put(node.module_name)
|
||||
self.put(u" cimport ")
|
||||
first = True
|
||||
for pos, name, as_name, kind in node.imported_names:
|
||||
assert kind is None
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
self.put(u", ")
|
||||
self.put(name)
|
||||
if as_name:
|
||||
self.put(u" as ")
|
||||
self.put(as_name)
|
||||
self.endline()
|
||||
|
||||
def visit_NameNode(self, node):
|
||||
self.put(node.name)
|
||||
|
||||
def visit_IntNode(self, node):
|
||||
self.put(node.value)
|
||||
|
||||
def visit_NoneNode(self, node):
|
||||
self.put(u"None")
|
||||
|
||||
def visit_NotNode(self, node):
|
||||
self.put(u"(not ")
|
||||
self.visit(node.operand)
|
||||
self.put(u")")
|
||||
|
||||
def visit_DecoratorNode(self, node):
|
||||
self.startline("@")
|
||||
self.visit(node.decorator)
|
||||
self.endline()
|
||||
|
||||
def visit_BinopNode(self, node):
|
||||
self.visit(node.operand1)
|
||||
self.put(u" %s " % node.operator)
|
||||
self.visit(node.operand2)
|
||||
|
||||
def visit_AttributeNode(self, node):
|
||||
self.visit(node.obj)
|
||||
self.put(u".%s" % node.attribute)
|
||||
|
||||
def visit_BoolNode(self, node):
|
||||
self.put(str(node.value))
|
||||
|
||||
# FIXME: represent string nodes correctly
|
||||
def visit_StringNode(self, node):
|
||||
value = node.value
|
||||
if value.encoding is not None:
|
||||
value = value.encode(value.encoding)
|
||||
self.put(repr(value))
|
||||
|
||||
def visit_PassStatNode(self, node):
|
||||
self.startline(u"pass")
|
||||
self.endline()
|
||||
|
||||
class CodeWriter(DeclarationWriter):
|
||||
|
||||
def visit_SingleAssignmentNode(self, node):
|
||||
self.startline()
|
||||
self.visit(node.lhs)
|
||||
self.put(u" = ")
|
||||
self.visit(node.rhs)
|
||||
self.endline()
|
||||
|
||||
def visit_CascadedAssignmentNode(self, node):
|
||||
self.startline()
|
||||
for lhs in node.lhs_list:
|
||||
self.visit(lhs)
|
||||
self.put(u" = ")
|
||||
self.visit(node.rhs)
|
||||
self.endline()
|
||||
|
||||
def visit_PrintStatNode(self, node):
|
||||
self.startline(u"print ")
|
||||
self.comma_separated_list(node.arg_tuple.args)
|
||||
if not node.append_newline:
|
||||
self.put(u",")
|
||||
self.endline()
|
||||
|
||||
def visit_ForInStatNode(self, node):
|
||||
self.startline(u"for ")
|
||||
self.visit(node.target)
|
||||
self.put(u" in ")
|
||||
self.visit(node.iterator.sequence)
|
||||
self.endline(u":")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
if node.else_clause is not None:
|
||||
self.line(u"else:")
|
||||
self.indent()
|
||||
self.visit(node.else_clause)
|
||||
self.dedent()
|
||||
|
||||
def visit_IfStatNode(self, node):
|
||||
# The IfClauseNode is handled directly without a separate match
|
||||
# for clariy.
|
||||
self.startline(u"if ")
|
||||
self.visit(node.if_clauses[0].condition)
|
||||
self.endline(":")
|
||||
self.indent()
|
||||
self.visit(node.if_clauses[0].body)
|
||||
self.dedent()
|
||||
for clause in node.if_clauses[1:]:
|
||||
self.startline("elif ")
|
||||
self.visit(clause.condition)
|
||||
self.endline(":")
|
||||
self.indent()
|
||||
self.visit(clause.body)
|
||||
self.dedent()
|
||||
if node.else_clause is not None:
|
||||
self.line("else:")
|
||||
self.indent()
|
||||
self.visit(node.else_clause)
|
||||
self.dedent()
|
||||
|
||||
def visit_SequenceNode(self, node):
|
||||
self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
|
||||
|
||||
def visit_SimpleCallNode(self, node):
|
||||
self.visit(node.function)
|
||||
self.put(u"(")
|
||||
self.comma_separated_list(node.args)
|
||||
self.put(")")
|
||||
|
||||
def visit_GeneralCallNode(self, node):
|
||||
self.visit(node.function)
|
||||
self.put(u"(")
|
||||
posarg = node.positional_args
|
||||
if isinstance(posarg, AsTupleNode):
|
||||
self.visit(posarg.arg)
|
||||
else:
|
||||
self.comma_separated_list(posarg.args) # TupleNode.args
|
||||
if node.keyword_args:
|
||||
if isinstance(node.keyword_args, DictNode):
|
||||
for i, (name, value) in enumerate(node.keyword_args.key_value_pairs):
|
||||
if i > 0:
|
||||
self.put(', ')
|
||||
self.visit(name)
|
||||
self.put('=')
|
||||
self.visit(value)
|
||||
else:
|
||||
raise Exception("Not implemented yet")
|
||||
self.put(u")")
|
||||
|
||||
def visit_ExprStatNode(self, node):
|
||||
self.startline()
|
||||
self.visit(node.expr)
|
||||
self.endline()
|
||||
|
||||
def visit_InPlaceAssignmentNode(self, node):
|
||||
self.startline()
|
||||
self.visit(node.lhs)
|
||||
self.put(u" %s= " % node.operator)
|
||||
self.visit(node.rhs)
|
||||
self.endline()
|
||||
|
||||
def visit_WithStatNode(self, node):
|
||||
self.startline()
|
||||
self.put(u"with ")
|
||||
self.visit(node.manager)
|
||||
if node.target is not None:
|
||||
self.put(u" as ")
|
||||
self.visit(node.target)
|
||||
self.endline(u":")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
|
||||
def visit_TryFinallyStatNode(self, node):
|
||||
self.line(u"try:")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
self.line(u"finally:")
|
||||
self.indent()
|
||||
self.visit(node.finally_clause)
|
||||
self.dedent()
|
||||
|
||||
def visit_TryExceptStatNode(self, node):
|
||||
self.line(u"try:")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
for x in node.except_clauses:
|
||||
self.visit(x)
|
||||
if node.else_clause is not None:
|
||||
self.visit(node.else_clause)
|
||||
|
||||
def visit_ExceptClauseNode(self, node):
|
||||
self.startline(u"except")
|
||||
if node.pattern is not None:
|
||||
self.put(u" ")
|
||||
self.visit(node.pattern)
|
||||
if node.target is not None:
|
||||
self.put(u", ")
|
||||
self.visit(node.target)
|
||||
self.endline(":")
|
||||
self.indent()
|
||||
self.visit(node.body)
|
||||
self.dedent()
|
||||
|
||||
def visit_ReturnStatNode(self, node):
|
||||
self.startline("return ")
|
||||
self.visit(node.value)
|
||||
self.endline()
|
||||
|
||||
def visit_ReraiseStatNode(self, node):
|
||||
self.line("raise")
|
||||
|
||||
def visit_ImportNode(self, node):
|
||||
self.put(u"(import %s)" % node.module_name.value)
|
||||
|
||||
def visit_TempsBlockNode(self, node):
|
||||
"""
|
||||
Temporaries are output like $1_1', where the first number is
|
||||
an index of the TempsBlockNode and the second number is an index
|
||||
of the temporary which that block allocates.
|
||||
"""
|
||||
idx = 0
|
||||
for handle in node.temps:
|
||||
self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx)
|
||||
idx += 1
|
||||
self.tempblockindex += 1
|
||||
self.visit(node.body)
|
||||
|
||||
def visit_TempRefNode(self, node):
|
||||
self.put(self.tempnames[node.handle])
|
||||
|
||||
|
||||
class PxdWriter(DeclarationWriter):
|
||||
def __call__(self, node):
|
||||
print(u'\n'.join(self.write(node).lines))
|
||||
return node
|
||||
|
||||
def visit_CFuncDefNode(self, node):
|
||||
if 'inline' in node.modifiers:
|
||||
return
|
||||
if node.overridable:
|
||||
self.startline(u'cpdef ')
|
||||
else:
|
||||
self.startline(u'cdef ')
|
||||
if node.visibility != 'private':
|
||||
self.put(node.visibility)
|
||||
self.put(u' ')
|
||||
if node.api:
|
||||
self.put(u'api ')
|
||||
self.visit(node.declarator)
|
||||
|
||||
def visit_StatNode(self, node):
|
||||
pass
|
||||
|
||||
|
||||
class ExpressionWriter(TreeVisitor):
|
||||
|
||||
def __init__(self, result=None):
|
||||
super(ExpressionWriter, self).__init__()
|
||||
if result is None:
|
||||
result = u""
|
||||
self.result = result
|
||||
self.precedence = [0]
|
||||
|
||||
def write(self, tree):
|
||||
self.visit(tree)
|
||||
return self.result
|
||||
|
||||
def put(self, s):
|
||||
self.result += s
|
||||
|
||||
def remove(self, s):
|
||||
if self.result.endswith(s):
|
||||
self.result = self.result[:-len(s)]
|
||||
|
||||
def comma_separated_list(self, items):
|
||||
if len(items) > 0:
|
||||
for item in items[:-1]:
|
||||
self.visit(item)
|
||||
self.put(u", ")
|
||||
self.visit(items[-1])
|
||||
|
||||
def visit_Node(self, node):
|
||||
raise AssertionError("Node not handled by serializer: %r" % node)
|
||||
|
||||
def visit_NameNode(self, node):
|
||||
self.put(node.name)
|
||||
|
||||
def visit_NoneNode(self, node):
|
||||
self.put(u"None")
|
||||
|
||||
def visit_EllipsisNode(self, node):
|
||||
self.put(u"...")
|
||||
|
||||
def visit_BoolNode(self, node):
|
||||
self.put(str(node.value))
|
||||
|
||||
def visit_ConstNode(self, node):
|
||||
self.put(str(node.value))
|
||||
|
||||
def visit_ImagNode(self, node):
|
||||
self.put(node.value)
|
||||
self.put(u"j")
|
||||
|
||||
def emit_string(self, node, prefix=u""):
|
||||
repr_val = repr(node.value)
|
||||
if repr_val[0] in 'ub':
|
||||
repr_val = repr_val[1:]
|
||||
self.put(u"%s%s" % (prefix, repr_val))
|
||||
|
||||
def visit_BytesNode(self, node):
|
||||
self.emit_string(node, u"b")
|
||||
|
||||
def visit_StringNode(self, node):
|
||||
self.emit_string(node)
|
||||
|
||||
def visit_UnicodeNode(self, node):
|
||||
self.emit_string(node, u"u")
|
||||
|
||||
def emit_sequence(self, node, parens=(u"", u"")):
|
||||
open_paren, close_paren = parens
|
||||
items = node.subexpr_nodes()
|
||||
self.put(open_paren)
|
||||
self.comma_separated_list(items)
|
||||
self.put(close_paren)
|
||||
|
||||
def visit_ListNode(self, node):
|
||||
self.emit_sequence(node, u"[]")
|
||||
|
||||
def visit_TupleNode(self, node):
|
||||
self.emit_sequence(node, u"()")
|
||||
|
||||
def visit_SetNode(self, node):
|
||||
if len(node.subexpr_nodes()) > 0:
|
||||
self.emit_sequence(node, u"{}")
|
||||
else:
|
||||
self.put(u"set()")
|
||||
|
||||
def visit_DictNode(self, node):
|
||||
self.emit_sequence(node, u"{}")
|
||||
|
||||
def visit_DictItemNode(self, node):
|
||||
self.visit(node.key)
|
||||
self.put(u": ")
|
||||
self.visit(node.value)
|
||||
|
||||
unop_precedence = {
|
||||
'not': 3, '!': 3,
|
||||
'+': 11, '-': 11, '~': 11,
|
||||
}
|
||||
binop_precedence = {
|
||||
'or': 1,
|
||||
'and': 2,
|
||||
# unary: 'not': 3, '!': 3,
|
||||
'in': 4, 'not_in': 4, 'is': 4, 'is_not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4,
|
||||
'|': 5,
|
||||
'^': 6,
|
||||
'&': 7,
|
||||
'<<': 8, '>>': 8,
|
||||
'+': 9, '-': 9,
|
||||
'*': 10, '@': 10, '/': 10, '//': 10, '%': 10,
|
||||
# unary: '+': 11, '-': 11, '~': 11
|
||||
'**': 12,
|
||||
}
|
||||
|
||||
def operator_enter(self, new_prec):
|
||||
old_prec = self.precedence[-1]
|
||||
if old_prec > new_prec:
|
||||
self.put(u"(")
|
||||
self.precedence.append(new_prec)
|
||||
|
||||
def operator_exit(self):
|
||||
old_prec, new_prec = self.precedence[-2:]
|
||||
if old_prec > new_prec:
|
||||
self.put(u")")
|
||||
self.precedence.pop()
|
||||
|
||||
def visit_NotNode(self, node):
|
||||
op = 'not'
|
||||
prec = self.unop_precedence[op]
|
||||
self.operator_enter(prec)
|
||||
self.put(u"not ")
|
||||
self.visit(node.operand)
|
||||
self.operator_exit()
|
||||
|
||||
def visit_UnopNode(self, node):
|
||||
op = node.operator
|
||||
prec = self.unop_precedence[op]
|
||||
self.operator_enter(prec)
|
||||
self.put(u"%s" % node.operator)
|
||||
self.visit(node.operand)
|
||||
self.operator_exit()
|
||||
|
||||
def visit_BinopNode(self, node):
|
||||
op = node.operator
|
||||
prec = self.binop_precedence.get(op, 0)
|
||||
self.operator_enter(prec)
|
||||
self.visit(node.operand1)
|
||||
self.put(u" %s " % op.replace('_', ' '))
|
||||
self.visit(node.operand2)
|
||||
self.operator_exit()
|
||||
|
||||
def visit_BoolBinopNode(self, node):
|
||||
self.visit_BinopNode(node)
|
||||
|
||||
def visit_PrimaryCmpNode(self, node):
|
||||
self.visit_BinopNode(node)
|
||||
|
||||
def visit_IndexNode(self, node):
|
||||
self.visit(node.base)
|
||||
self.put(u"[")
|
||||
if isinstance(node.index, TupleNode):
|
||||
self.emit_sequence(node.index)
|
||||
else:
|
||||
self.visit(node.index)
|
||||
self.put(u"]")
|
||||
|
||||
def visit_SliceIndexNode(self, node):
|
||||
self.visit(node.base)
|
||||
self.put(u"[")
|
||||
if node.start:
|
||||
self.visit(node.start)
|
||||
self.put(u":")
|
||||
if node.stop:
|
||||
self.visit(node.stop)
|
||||
if node.slice:
|
||||
self.put(u":")
|
||||
self.visit(node.slice)
|
||||
self.put(u"]")
|
||||
|
||||
def visit_SliceNode(self, node):
|
||||
if not node.start.is_none:
|
||||
self.visit(node.start)
|
||||
self.put(u":")
|
||||
if not node.stop.is_none:
|
||||
self.visit(node.stop)
|
||||
if not node.step.is_none:
|
||||
self.put(u":")
|
||||
self.visit(node.step)
|
||||
|
||||
def visit_CondExprNode(self, node):
|
||||
self.visit(node.true_val)
|
||||
self.put(u" if ")
|
||||
self.visit(node.test)
|
||||
self.put(u" else ")
|
||||
self.visit(node.false_val)
|
||||
|
||||
def visit_AttributeNode(self, node):
|
||||
self.visit(node.obj)
|
||||
self.put(u".%s" % node.attribute)
|
||||
|
||||
def visit_SimpleCallNode(self, node):
|
||||
self.visit(node.function)
|
||||
self.put(u"(")
|
||||
self.comma_separated_list(node.args)
|
||||
self.put(")")
|
||||
|
||||
def emit_pos_args(self, node):
|
||||
if node is None:
|
||||
return
|
||||
if isinstance(node, AddNode):
|
||||
self.emit_pos_args(node.operand1)
|
||||
self.emit_pos_args(node.operand2)
|
||||
elif isinstance(node, TupleNode):
|
||||
for expr in node.subexpr_nodes():
|
||||
self.visit(expr)
|
||||
self.put(u", ")
|
||||
elif isinstance(node, AsTupleNode):
|
||||
self.put("*")
|
||||
self.visit(node.arg)
|
||||
self.put(u", ")
|
||||
else:
|
||||
self.visit(node)
|
||||
self.put(u", ")
|
||||
|
||||
def emit_kwd_args(self, node):
|
||||
if node is None:
|
||||
return
|
||||
if isinstance(node, MergedDictNode):
|
||||
for expr in node.subexpr_nodes():
|
||||
self.emit_kwd_args(expr)
|
||||
elif isinstance(node, DictNode):
|
||||
for expr in node.subexpr_nodes():
|
||||
self.put(u"%s=" % expr.key.value)
|
||||
self.visit(expr.value)
|
||||
self.put(u", ")
|
||||
else:
|
||||
self.put(u"**")
|
||||
self.visit(node)
|
||||
self.put(u", ")
|
||||
|
||||
def visit_GeneralCallNode(self, node):
|
||||
self.visit(node.function)
|
||||
self.put(u"(")
|
||||
self.emit_pos_args(node.positional_args)
|
||||
self.emit_kwd_args(node.keyword_args)
|
||||
self.remove(u", ")
|
||||
self.put(")")
|
||||
|
||||
def emit_comprehension(self, body, target,
|
||||
sequence, condition,
|
||||
parens=(u"", u"")):
|
||||
open_paren, close_paren = parens
|
||||
self.put(open_paren)
|
||||
self.visit(body)
|
||||
self.put(u" for ")
|
||||
self.visit(target)
|
||||
self.put(u" in ")
|
||||
self.visit(sequence)
|
||||
if condition:
|
||||
self.put(u" if ")
|
||||
self.visit(condition)
|
||||
self.put(close_paren)
|
||||
|
||||
def visit_ComprehensionAppendNode(self, node):
|
||||
self.visit(node.expr)
|
||||
|
||||
def visit_DictComprehensionAppendNode(self, node):
|
||||
self.visit(node.key_expr)
|
||||
self.put(u": ")
|
||||
self.visit(node.value_expr)
|
||||
|
||||
def visit_ComprehensionNode(self, node):
|
||||
tpmap = {'list': u"[]", 'dict': u"{}", 'set': u"{}"}
|
||||
parens = tpmap[node.type.py_type_name()]
|
||||
body = node.loop.body
|
||||
target = node.loop.target
|
||||
sequence = node.loop.iterator.sequence
|
||||
condition = None
|
||||
if hasattr(body, 'if_clauses'):
|
||||
# type(body) is Nodes.IfStatNode
|
||||
condition = body.if_clauses[0].condition
|
||||
body = body.if_clauses[0].body
|
||||
self.emit_comprehension(body, target, sequence, condition, parens)
|
||||
|
||||
def visit_GeneratorExpressionNode(self, node):
|
||||
body = node.loop.body
|
||||
target = node.loop.target
|
||||
sequence = node.loop.iterator.sequence
|
||||
condition = None
|
||||
if hasattr(body, 'if_clauses'):
|
||||
# type(body) is Nodes.IfStatNode
|
||||
condition = body.if_clauses[0].condition
|
||||
body = body.if_clauses[0].body.expr.arg
|
||||
elif hasattr(body, 'expr'):
|
||||
# type(body) is Nodes.ExprStatNode
|
||||
body = body.expr.arg
|
||||
self.emit_comprehension(body, target, sequence, condition, u"()")
|
||||
@@ -0,0 +1,99 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Visitor import ScopeTrackingTransform
|
||||
from .Nodes import StatListNode, SingleAssignmentNode, CFuncDefNode, DefNode
|
||||
from .ExprNodes import DictNode, DictItemNode, NameNode, UnicodeNode
|
||||
from .PyrexTypes import py_object_type
|
||||
from .StringEncoding import EncodedString
|
||||
from . import Symtab
|
||||
|
||||
class AutoTestDictTransform(ScopeTrackingTransform):
|
||||
# Handles autotestdict directive
|
||||
|
||||
blacklist = ['__cinit__', '__dealloc__', '__richcmp__',
|
||||
'__nonzero__', '__bool__',
|
||||
'__len__', '__contains__']
|
||||
|
||||
def visit_ModuleNode(self, node):
|
||||
if node.is_pxd:
|
||||
return node
|
||||
self.scope_type = 'module'
|
||||
self.scope_node = node
|
||||
|
||||
if not self.current_directives['autotestdict']:
|
||||
return node
|
||||
self.all_docstrings = self.current_directives['autotestdict.all']
|
||||
self.cdef_docstrings = self.all_docstrings or self.current_directives['autotestdict.cdef']
|
||||
|
||||
assert isinstance(node.body, StatListNode)
|
||||
|
||||
# First see if __test__ is already created
|
||||
if u'__test__' in node.scope.entries:
|
||||
# Do nothing
|
||||
return node
|
||||
|
||||
pos = node.pos
|
||||
|
||||
self.tests = []
|
||||
self.testspos = node.pos
|
||||
|
||||
test_dict_entry = node.scope.declare_var(EncodedString(u'__test__'),
|
||||
py_object_type,
|
||||
pos,
|
||||
visibility='public')
|
||||
create_test_dict_assignment = SingleAssignmentNode(pos,
|
||||
lhs=NameNode(pos, name=EncodedString(u'__test__'),
|
||||
entry=test_dict_entry),
|
||||
rhs=DictNode(pos, key_value_pairs=self.tests))
|
||||
self.visitchildren(node)
|
||||
node.body.stats.append(create_test_dict_assignment)
|
||||
return node
|
||||
|
||||
def add_test(self, testpos, path, doctest):
|
||||
pos = self.testspos
|
||||
keystr = u'%s (line %d)' % (path, testpos[1])
|
||||
key = UnicodeNode(pos, value=EncodedString(keystr))
|
||||
value = UnicodeNode(pos, value=doctest)
|
||||
self.tests.append(DictItemNode(pos, key=key, value=value))
|
||||
|
||||
def visit_ExprNode(self, node):
|
||||
# expressions cannot contain functions and lambda expressions
|
||||
# do not have a docstring
|
||||
return node
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
if not node.doc or (isinstance(node, DefNode) and node.fused_py_func):
|
||||
return node
|
||||
if not self.cdef_docstrings:
|
||||
if isinstance(node, CFuncDefNode) and not node.py_func:
|
||||
return node
|
||||
if not self.all_docstrings and '>>>' not in node.doc:
|
||||
return node
|
||||
|
||||
pos = self.testspos
|
||||
if self.scope_type == 'module':
|
||||
path = node.entry.name
|
||||
elif self.scope_type in ('pyclass', 'cclass'):
|
||||
if isinstance(node, CFuncDefNode):
|
||||
if node.py_func is not None:
|
||||
name = node.py_func.name
|
||||
else:
|
||||
name = node.entry.name
|
||||
else:
|
||||
name = node.name
|
||||
if self.scope_type == 'cclass' and name in self.blacklist:
|
||||
return node
|
||||
if self.scope_type == 'pyclass':
|
||||
class_name = self.scope_node.name
|
||||
else:
|
||||
class_name = self.scope_node.class_name
|
||||
if isinstance(node.entry.scope, Symtab.PropertyScope):
|
||||
property_method_name = node.entry.scope.name
|
||||
path = "%s.%s.%s" % (class_name, node.entry.scope.name,
|
||||
node.entry.name)
|
||||
else:
|
||||
path = "%s.%s" % (class_name, node.entry.name)
|
||||
else:
|
||||
assert False
|
||||
self.add_test(node.pos, path, node.doc)
|
||||
return node
|
||||
@@ -0,0 +1,317 @@
|
||||
# Note: Work in progress
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import codecs
|
||||
import textwrap
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from collections import defaultdict
|
||||
from xml.sax.saxutils import escape as html_escape
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO # does not support writing 'str' in Py2
|
||||
|
||||
from . import Version
|
||||
from .Code import CCodeWriter
|
||||
from .. import Utils
|
||||
|
||||
|
||||
class AnnotationCCodeWriter(CCodeWriter):
|
||||
|
||||
def __init__(self, create_from=None, buffer=None, copy_formatting=True):
|
||||
CCodeWriter.__init__(self, create_from, buffer, copy_formatting=copy_formatting)
|
||||
if create_from is None:
|
||||
self.annotation_buffer = StringIO()
|
||||
self.last_annotated_pos = None
|
||||
# annotations[filename][line] -> [(column, AnnotationItem)*]
|
||||
self.annotations = defaultdict(partial(defaultdict, list))
|
||||
# code[filename][line] -> str
|
||||
self.code = defaultdict(partial(defaultdict, str))
|
||||
# scopes[filename][line] -> set(scopes)
|
||||
self.scopes = defaultdict(partial(defaultdict, set))
|
||||
else:
|
||||
# When creating an insertion point, keep references to the same database
|
||||
self.annotation_buffer = create_from.annotation_buffer
|
||||
self.annotations = create_from.annotations
|
||||
self.code = create_from.code
|
||||
self.scopes = create_from.scopes
|
||||
self.last_annotated_pos = create_from.last_annotated_pos
|
||||
|
||||
def create_new(self, create_from, buffer, copy_formatting):
|
||||
return AnnotationCCodeWriter(create_from, buffer, copy_formatting)
|
||||
|
||||
def write(self, s):
|
||||
CCodeWriter.write(self, s)
|
||||
self.annotation_buffer.write(s)
|
||||
|
||||
def mark_pos(self, pos, trace=True):
|
||||
if pos is not None:
|
||||
CCodeWriter.mark_pos(self, pos, trace)
|
||||
if self.funcstate and self.funcstate.scope:
|
||||
# lambdas and genexprs can result in multiple scopes per line => keep them in a set
|
||||
self.scopes[pos[0].filename][pos[1]].add(self.funcstate.scope)
|
||||
if self.last_annotated_pos:
|
||||
source_desc, line, _ = self.last_annotated_pos
|
||||
pos_code = self.code[source_desc.filename]
|
||||
pos_code[line] += self.annotation_buffer.getvalue()
|
||||
self.annotation_buffer = StringIO()
|
||||
self.last_annotated_pos = pos
|
||||
|
||||
def annotate(self, pos, item):
|
||||
self.annotations[pos[0].filename][pos[1]].append((pos[2], item))
|
||||
|
||||
def _css(self):
|
||||
"""css template will later allow to choose a colormap"""
|
||||
css = [self._css_template]
|
||||
for i in range(255):
|
||||
color = u"FFFF%02x" % int(255/(1+i/10.0))
|
||||
css.append('.cython.score-%d {background-color: #%s;}' % (i, color))
|
||||
try:
|
||||
from pygments.formatters import HtmlFormatter
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
css.append(HtmlFormatter().get_style_defs('.cython'))
|
||||
return '\n'.join(css)
|
||||
|
||||
_css_template = textwrap.dedent("""
|
||||
body.cython { font-family: courier; font-size: 12; }
|
||||
|
||||
.cython.tag { }
|
||||
.cython.line { margin: 0em }
|
||||
.cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }
|
||||
|
||||
.cython.line .run { background-color: #B0FFB0; }
|
||||
.cython.line .mis { background-color: #FFB0B0; }
|
||||
.cython.code.run { border-left: 8px solid #B0FFB0; }
|
||||
.cython.code.mis { border-left: 8px solid #FFB0B0; }
|
||||
|
||||
.cython.code .py_c_api { color: red; }
|
||||
.cython.code .py_macro_api { color: #FF7000; }
|
||||
.cython.code .pyx_c_api { color: #FF3000; }
|
||||
.cython.code .pyx_macro_api { color: #FF7000; }
|
||||
.cython.code .refnanny { color: #FFA000; }
|
||||
.cython.code .trace { color: #FFA000; }
|
||||
.cython.code .error_goto { color: #FFA000; }
|
||||
|
||||
.cython.code .coerce { color: #008000; border: 1px dotted #008000 }
|
||||
.cython.code .py_attr { color: #FF0000; font-weight: bold; }
|
||||
.cython.code .c_attr { color: #0000FF; }
|
||||
.cython.code .py_call { color: #FF0000; font-weight: bold; }
|
||||
.cython.code .c_call { color: #0000FF; }
|
||||
""")
|
||||
|
||||
# on-click toggle function to show/hide C source code
|
||||
_onclick_attr = ' onclick="{0}"'.format((
|
||||
"(function(s){"
|
||||
" s.display = s.display === 'block' ? 'none' : 'block'"
|
||||
"})(this.nextElementSibling.style)"
|
||||
).replace(' ', '') # poor dev's JS minification
|
||||
)
|
||||
|
||||
def save_annotation(self, source_filename, target_filename, coverage_xml=None):
|
||||
with Utils.open_source_file(source_filename) as f:
|
||||
code = f.read()
|
||||
generated_code = self.code.get(source_filename, {})
|
||||
c_file = Utils.decode_filename(os.path.basename(target_filename))
|
||||
html_filename = os.path.splitext(target_filename)[0] + ".html"
|
||||
|
||||
with codecs.open(html_filename, "w", encoding="UTF-8") as out_buffer:
|
||||
out_buffer.write(self._save_annotation(code, generated_code, c_file, source_filename, coverage_xml))
|
||||
|
||||
def _save_annotation_header(self, c_file, source_filename, coverage_timestamp=None):
|
||||
coverage_info = ''
|
||||
if coverage_timestamp:
|
||||
coverage_info = u' with coverage data from {timestamp}'.format(
|
||||
timestamp=datetime.fromtimestamp(int(coverage_timestamp) // 1000))
|
||||
|
||||
outlist = [
|
||||
textwrap.dedent(u'''\
|
||||
<!DOCTYPE html>
|
||||
<!-- Generated by Cython {watermark} -->
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Cython: {filename}</title>
|
||||
<style type="text/css">
|
||||
{css}
|
||||
</style>
|
||||
</head>
|
||||
<body class="cython">
|
||||
<p><span style="border-bottom: solid 1px grey;">Generated by Cython {watermark}</span>{more_info}</p>
|
||||
<p>
|
||||
<span style="background-color: #FFFF00">Yellow lines</span> hint at Python interaction.<br />
|
||||
Click on a line that starts with a "<code>+</code>" to see the C code that Cython generated for it.
|
||||
</p>
|
||||
''').format(css=self._css(), watermark=Version.watermark,
|
||||
filename=os.path.basename(source_filename) if source_filename else '',
|
||||
more_info=coverage_info)
|
||||
]
|
||||
if c_file:
|
||||
outlist.append(u'<p>Raw output: <a href="%s">%s</a></p>\n' % (c_file, c_file))
|
||||
return outlist
|
||||
|
||||
def _save_annotation_footer(self):
|
||||
return (u'</body></html>\n',)
|
||||
|
||||
def _save_annotation(self, code, generated_code, c_file=None, source_filename=None, coverage_xml=None):
|
||||
"""
|
||||
lines : original cython source code split by lines
|
||||
generated_code : generated c code keyed by line number in original file
|
||||
target filename : name of the file in which to store the generated html
|
||||
c_file : filename in which the c_code has been written
|
||||
"""
|
||||
if coverage_xml is not None and source_filename:
|
||||
coverage_timestamp = coverage_xml.get('timestamp', '').strip()
|
||||
covered_lines = self._get_line_coverage(coverage_xml, source_filename)
|
||||
else:
|
||||
coverage_timestamp = covered_lines = None
|
||||
annotation_items = dict(self.annotations[source_filename])
|
||||
scopes = dict(self.scopes[source_filename])
|
||||
|
||||
outlist = []
|
||||
outlist.extend(self._save_annotation_header(c_file, source_filename, coverage_timestamp))
|
||||
outlist.extend(self._save_annotation_body(code, generated_code, annotation_items, scopes, covered_lines))
|
||||
outlist.extend(self._save_annotation_footer())
|
||||
return ''.join(outlist)
|
||||
|
||||
def _get_line_coverage(self, coverage_xml, source_filename):
|
||||
coverage_data = None
|
||||
for entry in coverage_xml.iterfind('.//class'):
|
||||
if not entry.get('filename'):
|
||||
continue
|
||||
if (entry.get('filename') == source_filename or
|
||||
os.path.abspath(entry.get('filename')) == source_filename):
|
||||
coverage_data = entry
|
||||
break
|
||||
elif source_filename.endswith(entry.get('filename')):
|
||||
coverage_data = entry # but we might still find a better match...
|
||||
if coverage_data is None:
|
||||
return None
|
||||
return dict(
|
||||
(int(line.get('number')), int(line.get('hits')))
|
||||
for line in coverage_data.iterfind('lines/line')
|
||||
)
|
||||
|
||||
def _htmlify_code(self, code):
|
||||
try:
|
||||
from pygments import highlight
|
||||
from pygments.lexers import CythonLexer
|
||||
from pygments.formatters import HtmlFormatter
|
||||
except ImportError:
|
||||
# no Pygments, just escape the code
|
||||
return html_escape(code)
|
||||
|
||||
html_code = highlight(
|
||||
code, CythonLexer(stripnl=False, stripall=False),
|
||||
HtmlFormatter(nowrap=True))
|
||||
return html_code
|
||||
|
||||
def _save_annotation_body(self, cython_code, generated_code, annotation_items, scopes, covered_lines=None):
|
||||
outlist = [u'<div class="cython">']
|
||||
pos_comment_marker = u'/* \N{HORIZONTAL ELLIPSIS} */\n'
|
||||
new_calls_map = dict(
|
||||
(name, 0) for name in
|
||||
'refnanny trace py_macro_api py_c_api pyx_macro_api pyx_c_api error_goto'.split()
|
||||
).copy
|
||||
|
||||
self.mark_pos(None)
|
||||
|
||||
def annotate(match):
|
||||
group_name = match.lastgroup
|
||||
calls[group_name] += 1
|
||||
return u"<span class='%s'>%s</span>" % (
|
||||
group_name, match.group(group_name))
|
||||
|
||||
lines = self._htmlify_code(cython_code).splitlines()
|
||||
lineno_width = len(str(len(lines)))
|
||||
if not covered_lines:
|
||||
covered_lines = None
|
||||
|
||||
for k, line in enumerate(lines, 1):
|
||||
try:
|
||||
c_code = generated_code[k]
|
||||
except KeyError:
|
||||
c_code = ''
|
||||
else:
|
||||
c_code = _replace_pos_comment(pos_comment_marker, c_code)
|
||||
if c_code.startswith(pos_comment_marker):
|
||||
c_code = c_code[len(pos_comment_marker):]
|
||||
c_code = html_escape(c_code)
|
||||
|
||||
calls = new_calls_map()
|
||||
c_code = _parse_code(annotate, c_code)
|
||||
score = (5 * calls['py_c_api'] + 2 * calls['pyx_c_api'] +
|
||||
calls['py_macro_api'] + calls['pyx_macro_api'])
|
||||
|
||||
if c_code:
|
||||
onclick = self._onclick_attr
|
||||
expandsymbol = '+'
|
||||
else:
|
||||
onclick = ''
|
||||
expandsymbol = ' '
|
||||
|
||||
covered = ''
|
||||
if covered_lines is not None and k in covered_lines:
|
||||
hits = covered_lines[k]
|
||||
if hits is not None:
|
||||
covered = 'run' if hits else 'mis'
|
||||
|
||||
outlist.append(
|
||||
u'<pre class="cython line score-{score}"{onclick}>'
|
||||
# generate line number with expand symbol in front,
|
||||
# and the right number of digit
|
||||
u'{expandsymbol}<span class="{covered}">{line:0{lineno_width}d}</span>: {code}</pre>\n'.format(
|
||||
score=score,
|
||||
expandsymbol=expandsymbol,
|
||||
covered=covered,
|
||||
lineno_width=lineno_width,
|
||||
line=k,
|
||||
code=line.rstrip(),
|
||||
onclick=onclick,
|
||||
))
|
||||
if c_code:
|
||||
outlist.append(u"<pre class='cython code score-{score} {covered}'>{code}</pre>".format(
|
||||
score=score, covered=covered, code=c_code))
|
||||
outlist.append(u"</div>")
|
||||
return outlist
|
||||
|
||||
|
||||
_parse_code = re.compile((
|
||||
br'(?P<refnanny>__Pyx_X?(?:GOT|GIVE)REF|__Pyx_RefNanny[A-Za-z]+)|'
|
||||
br'(?P<trace>__Pyx_Trace[A-Za-z]+)|'
|
||||
br'(?:'
|
||||
br'(?P<pyx_macro_api>__Pyx_[A-Z][A-Z_]+)|'
|
||||
br'(?P<pyx_c_api>(?:__Pyx_[A-Z][a-z_][A-Za-z_]*)|__pyx_convert_[A-Za-z_]*)|'
|
||||
br'(?P<py_macro_api>Py[A-Z][a-z]+_[A-Z][A-Z_]+)|'
|
||||
br'(?P<py_c_api>Py[A-Z][a-z]+_[A-Z][a-z][A-Za-z_]*)'
|
||||
br')(?=\()|' # look-ahead to exclude subsequent '(' from replacement
|
||||
br'(?P<error_goto>(?:(?<=;) *if [^;]* +)?__PYX_ERR\([^)]+\))'
|
||||
).decode('ascii')).sub
|
||||
|
||||
|
||||
_replace_pos_comment = re.compile(
|
||||
# this matches what Cython generates as code line marker comment
|
||||
br'^\s*/\*(?:(?:[^*]|\*[^/])*\n)+\s*\*/\s*\n'.decode('ascii'),
|
||||
re.M
|
||||
).sub
|
||||
|
||||
|
||||
class AnnotationItem(object):
|
||||
|
||||
def __init__(self, style, text, tag="", size=0):
|
||||
self.style = style
|
||||
self.text = text
|
||||
self.tag = tag
|
||||
self.size = size
|
||||
|
||||
def start(self):
|
||||
return u"<span class='cython tag %s' title='%s'>%s" % (self.style, self.text, self.tag)
|
||||
|
||||
def end(self):
|
||||
return self.size, u"</span>"
|
||||
@@ -0,0 +1,214 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from .Visitor import CythonTransform
|
||||
from .StringEncoding import EncodedString
|
||||
from . import Options
|
||||
from . import PyrexTypes, ExprNodes
|
||||
from ..CodeWriter import ExpressionWriter
|
||||
|
||||
|
||||
class AnnotationWriter(ExpressionWriter):
|
||||
|
||||
def visit_Node(self, node):
|
||||
self.put(u"<???>")
|
||||
|
||||
def visit_LambdaNode(self, node):
|
||||
# XXX Should we do better?
|
||||
self.put("<lambda>")
|
||||
|
||||
|
||||
class EmbedSignature(CythonTransform):
|
||||
|
||||
def __init__(self, context):
|
||||
super(EmbedSignature, self).__init__(context)
|
||||
self.class_name = None
|
||||
self.class_node = None
|
||||
|
||||
def _fmt_expr(self, node):
|
||||
writer = AnnotationWriter()
|
||||
result = writer.write(node)
|
||||
# print(type(node).__name__, '-->', result)
|
||||
return result
|
||||
|
||||
def _fmt_arg(self, arg):
|
||||
if arg.type is PyrexTypes.py_object_type or arg.is_self_arg:
|
||||
doc = arg.name
|
||||
else:
|
||||
doc = arg.type.declaration_code(arg.name, for_display=1)
|
||||
|
||||
if arg.annotation:
|
||||
annotation = self._fmt_expr(arg.annotation)
|
||||
doc = doc + (': %s' % annotation)
|
||||
if arg.default:
|
||||
default = self._fmt_expr(arg.default)
|
||||
doc = doc + (' = %s' % default)
|
||||
elif arg.default:
|
||||
default = self._fmt_expr(arg.default)
|
||||
doc = doc + ('=%s' % default)
|
||||
return doc
|
||||
|
||||
def _fmt_star_arg(self, arg):
|
||||
arg_doc = arg.name
|
||||
if arg.annotation:
|
||||
annotation = self._fmt_expr(arg.annotation)
|
||||
arg_doc = arg_doc + (': %s' % annotation)
|
||||
return arg_doc
|
||||
|
||||
def _fmt_arglist(self, args,
|
||||
npargs=0, pargs=None,
|
||||
nkargs=0, kargs=None,
|
||||
hide_self=False):
|
||||
arglist = []
|
||||
for arg in args:
|
||||
if not hide_self or not arg.entry.is_self_arg:
|
||||
arg_doc = self._fmt_arg(arg)
|
||||
arglist.append(arg_doc)
|
||||
if pargs:
|
||||
arg_doc = self._fmt_star_arg(pargs)
|
||||
arglist.insert(npargs, '*%s' % arg_doc)
|
||||
elif nkargs:
|
||||
arglist.insert(npargs, '*')
|
||||
if kargs:
|
||||
arg_doc = self._fmt_star_arg(kargs)
|
||||
arglist.append('**%s' % arg_doc)
|
||||
return arglist
|
||||
|
||||
def _fmt_ret_type(self, ret):
|
||||
if ret is PyrexTypes.py_object_type:
|
||||
return None
|
||||
else:
|
||||
return ret.declaration_code("", for_display=1)
|
||||
|
||||
def _fmt_signature(self, cls_name, func_name, args,
|
||||
npargs=0, pargs=None,
|
||||
nkargs=0, kargs=None,
|
||||
return_expr=None,
|
||||
return_type=None, hide_self=False):
|
||||
arglist = self._fmt_arglist(args,
|
||||
npargs, pargs,
|
||||
nkargs, kargs,
|
||||
hide_self=hide_self)
|
||||
arglist_doc = ', '.join(arglist)
|
||||
func_doc = '%s(%s)' % (func_name, arglist_doc)
|
||||
if cls_name:
|
||||
func_doc = '%s.%s' % (cls_name, func_doc)
|
||||
ret_doc = None
|
||||
if return_expr:
|
||||
ret_doc = self._fmt_expr(return_expr)
|
||||
elif return_type:
|
||||
ret_doc = self._fmt_ret_type(return_type)
|
||||
if ret_doc:
|
||||
func_doc = '%s -> %s' % (func_doc, ret_doc)
|
||||
return func_doc
|
||||
|
||||
def _embed_signature(self, signature, node_doc):
|
||||
if node_doc:
|
||||
return "%s\n%s" % (signature, node_doc)
|
||||
else:
|
||||
return signature
|
||||
|
||||
def __call__(self, node):
|
||||
if not Options.docstrings:
|
||||
return node
|
||||
else:
|
||||
return super(EmbedSignature, self).__call__(node)
|
||||
|
||||
def visit_ClassDefNode(self, node):
|
||||
oldname = self.class_name
|
||||
oldclass = self.class_node
|
||||
self.class_node = node
|
||||
try:
|
||||
# PyClassDefNode
|
||||
self.class_name = node.name
|
||||
except AttributeError:
|
||||
# CClassDefNode
|
||||
self.class_name = node.class_name
|
||||
self.visitchildren(node)
|
||||
self.class_name = oldname
|
||||
self.class_node = oldclass
|
||||
return node
|
||||
|
||||
def visit_LambdaNode(self, node):
|
||||
# lambda expressions so not have signature or inner functions
|
||||
return node
|
||||
|
||||
def visit_DefNode(self, node):
|
||||
if not self.current_directives['embedsignature']:
|
||||
return node
|
||||
|
||||
is_constructor = False
|
||||
hide_self = False
|
||||
if node.entry.is_special:
|
||||
is_constructor = self.class_node and node.name == '__init__'
|
||||
if not is_constructor:
|
||||
return node
|
||||
class_name, func_name = None, self.class_name
|
||||
hide_self = True
|
||||
else:
|
||||
class_name, func_name = self.class_name, node.name
|
||||
|
||||
nkargs = getattr(node, 'num_kwonly_args', 0)
|
||||
npargs = len(node.args) - nkargs
|
||||
signature = self._fmt_signature(
|
||||
class_name, func_name, node.args,
|
||||
npargs, node.star_arg,
|
||||
nkargs, node.starstar_arg,
|
||||
return_expr=node.return_type_annotation,
|
||||
return_type=None, hide_self=hide_self)
|
||||
if signature:
|
||||
if is_constructor:
|
||||
doc_holder = self.class_node.entry.type.scope
|
||||
else:
|
||||
doc_holder = node.entry
|
||||
|
||||
if doc_holder.doc is not None:
|
||||
old_doc = doc_holder.doc
|
||||
elif not is_constructor and getattr(node, 'py_func', None) is not None:
|
||||
old_doc = node.py_func.entry.doc
|
||||
else:
|
||||
old_doc = None
|
||||
new_doc = self._embed_signature(signature, old_doc)
|
||||
doc_holder.doc = EncodedString(new_doc)
|
||||
if not is_constructor and getattr(node, 'py_func', None) is not None:
|
||||
node.py_func.entry.doc = EncodedString(new_doc)
|
||||
return node
|
||||
|
||||
def visit_CFuncDefNode(self, node):
|
||||
if not self.current_directives['embedsignature']:
|
||||
return node
|
||||
if not node.overridable: # not cpdef FOO(...):
|
||||
return node
|
||||
|
||||
signature = self._fmt_signature(
|
||||
self.class_name, node.declarator.base.name,
|
||||
node.declarator.args,
|
||||
return_type=node.return_type)
|
||||
if signature:
|
||||
if node.entry.doc is not None:
|
||||
old_doc = node.entry.doc
|
||||
elif getattr(node, 'py_func', None) is not None:
|
||||
old_doc = node.py_func.entry.doc
|
||||
else:
|
||||
old_doc = None
|
||||
new_doc = self._embed_signature(signature, old_doc)
|
||||
node.entry.doc = EncodedString(new_doc)
|
||||
if hasattr(node, 'py_func') and node.py_func is not None:
|
||||
node.py_func.entry.doc = EncodedString(new_doc)
|
||||
return node
|
||||
|
||||
def visit_PropertyNode(self, node):
|
||||
if not self.current_directives['embedsignature']:
|
||||
return node
|
||||
|
||||
entry = node.entry
|
||||
if entry.visibility == 'public':
|
||||
# property synthesised from a cdef public attribute
|
||||
type_name = entry.type.declaration_code("", for_display=1)
|
||||
if not entry.type.is_pyobject:
|
||||
type_name = "'%s'" % type_name
|
||||
elif entry.type.is_extension_type:
|
||||
type_name = entry.type.module_name + '.' + type_name
|
||||
signature = '%s: %s' % (entry.name, type_name)
|
||||
new_doc = self._embed_signature(signature, entry.doc)
|
||||
entry.doc = EncodedString(new_doc)
|
||||
return node
|
||||
@@ -0,0 +1,740 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Visitor import CythonTransform
|
||||
from .ModuleNode import ModuleNode
|
||||
from .Errors import CompileError
|
||||
from .UtilityCode import CythonUtilityCode
|
||||
from .Code import UtilityCode, TempitaUtilityCode
|
||||
|
||||
from . import Options
|
||||
from . import Interpreter
|
||||
from . import PyrexTypes
|
||||
from . import Naming
|
||||
from . import Symtab
|
||||
|
||||
def dedent(text, reindent=0):
|
||||
from textwrap import dedent
|
||||
text = dedent(text)
|
||||
if reindent > 0:
|
||||
indent = " " * reindent
|
||||
text = '\n'.join([indent + x for x in text.split('\n')])
|
||||
return text
|
||||
|
||||
class IntroduceBufferAuxiliaryVars(CythonTransform):
|
||||
|
||||
#
|
||||
# Entry point
|
||||
#
|
||||
|
||||
buffers_exists = False
|
||||
using_memoryview = False
|
||||
|
||||
def __call__(self, node):
|
||||
assert isinstance(node, ModuleNode)
|
||||
self.max_ndim = 0
|
||||
result = super(IntroduceBufferAuxiliaryVars, self).__call__(node)
|
||||
if self.buffers_exists:
|
||||
use_bufstruct_declare_code(node.scope)
|
||||
use_py2_buffer_functions(node.scope)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
#
|
||||
# Basic operations for transforms
|
||||
#
|
||||
def handle_scope(self, node, scope):
|
||||
# For all buffers, insert extra variables in the scope.
|
||||
# The variables are also accessible from the buffer_info
|
||||
# on the buffer entry
|
||||
scope_items = scope.entries.items()
|
||||
bufvars = [entry for name, entry in scope_items if entry.type.is_buffer]
|
||||
if len(bufvars) > 0:
|
||||
bufvars.sort(key=lambda entry: entry.name)
|
||||
self.buffers_exists = True
|
||||
|
||||
memviewslicevars = [entry for name, entry in scope_items if entry.type.is_memoryviewslice]
|
||||
if len(memviewslicevars) > 0:
|
||||
self.buffers_exists = True
|
||||
|
||||
|
||||
for (name, entry) in scope_items:
|
||||
if name == 'memoryview' and isinstance(entry.utility_code_definition, CythonUtilityCode):
|
||||
self.using_memoryview = True
|
||||
break
|
||||
del scope_items
|
||||
|
||||
if isinstance(node, ModuleNode) and len(bufvars) > 0:
|
||||
# for now...note that pos is wrong
|
||||
raise CompileError(node.pos, "Buffer vars not allowed in module scope")
|
||||
for entry in bufvars:
|
||||
if entry.type.dtype.is_ptr:
|
||||
raise CompileError(node.pos, "Buffers with pointer types not yet supported.")
|
||||
|
||||
name = entry.name
|
||||
buftype = entry.type
|
||||
if buftype.ndim > Options.buffer_max_dims:
|
||||
raise CompileError(node.pos,
|
||||
"Buffer ndims exceeds Options.buffer_max_dims = %d" % Options.buffer_max_dims)
|
||||
if buftype.ndim > self.max_ndim:
|
||||
self.max_ndim = buftype.ndim
|
||||
|
||||
# Declare auxiliary vars
|
||||
def decvar(type, prefix):
|
||||
cname = scope.mangle(prefix, name)
|
||||
aux_var = scope.declare_var(name=None, cname=cname,
|
||||
type=type, pos=node.pos)
|
||||
if entry.is_arg:
|
||||
aux_var.used = True # otherwise, NameNode will mark whether it is used
|
||||
|
||||
return aux_var
|
||||
|
||||
auxvars = ((PyrexTypes.c_pyx_buffer_nd_type, Naming.pybuffernd_prefix),
|
||||
(PyrexTypes.c_pyx_buffer_type, Naming.pybufferstruct_prefix))
|
||||
pybuffernd, rcbuffer = [decvar(type, prefix) for (type, prefix) in auxvars]
|
||||
|
||||
entry.buffer_aux = Symtab.BufferAux(pybuffernd, rcbuffer)
|
||||
|
||||
scope.buffer_entries = bufvars
|
||||
self.scope = scope
|
||||
|
||||
def visit_ModuleNode(self, node):
|
||||
self.handle_scope(node, node.scope)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
self.handle_scope(node, node.local_scope)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
#
|
||||
# Analysis
|
||||
#
|
||||
buffer_options = ("dtype", "ndim", "mode", "negative_indices", "cast") # ordered!
|
||||
buffer_defaults = {"ndim": 1, "mode": "full", "negative_indices": True, "cast": False}
|
||||
buffer_positional_options_count = 1 # anything beyond this needs keyword argument
|
||||
|
||||
ERR_BUF_OPTION_UNKNOWN = '"%s" is not a buffer option'
|
||||
ERR_BUF_TOO_MANY = 'Too many buffer options'
|
||||
ERR_BUF_DUP = '"%s" buffer option already supplied'
|
||||
ERR_BUF_MISSING = '"%s" missing'
|
||||
ERR_BUF_MODE = 'Only allowed buffer modes are: "c", "fortran", "full", "strided" (as a compile-time string)'
|
||||
ERR_BUF_NDIM = 'ndim must be a non-negative integer'
|
||||
ERR_BUF_DTYPE = 'dtype must be "object", numeric type or a struct'
|
||||
ERR_BUF_BOOL = '"%s" must be a boolean'
|
||||
|
||||
def analyse_buffer_options(globalpos, env, posargs, dictargs, defaults=None, need_complete=True):
|
||||
"""
|
||||
Must be called during type analysis, as analyse is called
|
||||
on the dtype argument.
|
||||
|
||||
posargs and dictargs should consist of a list and a dict
|
||||
of tuples (value, pos). Defaults should be a dict of values.
|
||||
|
||||
Returns a dict containing all the options a buffer can have and
|
||||
its value (with the positions stripped).
|
||||
"""
|
||||
if defaults is None:
|
||||
defaults = buffer_defaults
|
||||
|
||||
posargs, dictargs = Interpreter.interpret_compiletime_options(
|
||||
posargs, dictargs, type_env=env, type_args=(0, 'dtype'))
|
||||
|
||||
if len(posargs) > buffer_positional_options_count:
|
||||
raise CompileError(posargs[-1][1], ERR_BUF_TOO_MANY)
|
||||
|
||||
options = {}
|
||||
for name, (value, pos) in dictargs.items():
|
||||
if not name in buffer_options:
|
||||
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
|
||||
options[name] = value
|
||||
|
||||
for name, (value, pos) in zip(buffer_options, posargs):
|
||||
if not name in buffer_options:
|
||||
raise CompileError(pos, ERR_BUF_OPTION_UNKNOWN % name)
|
||||
if name in options:
|
||||
raise CompileError(pos, ERR_BUF_DUP % name)
|
||||
options[name] = value
|
||||
|
||||
# Check that they are all there and copy defaults
|
||||
for name in buffer_options:
|
||||
if not name in options:
|
||||
try:
|
||||
options[name] = defaults[name]
|
||||
except KeyError:
|
||||
if need_complete:
|
||||
raise CompileError(globalpos, ERR_BUF_MISSING % name)
|
||||
|
||||
dtype = options.get("dtype")
|
||||
if dtype and dtype.is_extension_type:
|
||||
raise CompileError(globalpos, ERR_BUF_DTYPE)
|
||||
|
||||
ndim = options.get("ndim")
|
||||
if ndim and (not isinstance(ndim, int) or ndim < 0):
|
||||
raise CompileError(globalpos, ERR_BUF_NDIM)
|
||||
|
||||
mode = options.get("mode")
|
||||
if mode and not (mode in ('full', 'strided', 'c', 'fortran')):
|
||||
raise CompileError(globalpos, ERR_BUF_MODE)
|
||||
|
||||
def assert_bool(name):
|
||||
x = options.get(name)
|
||||
if not isinstance(x, bool):
|
||||
raise CompileError(globalpos, ERR_BUF_BOOL % name)
|
||||
|
||||
assert_bool('negative_indices')
|
||||
assert_bool('cast')
|
||||
|
||||
return options
|
||||
|
||||
|
||||
#
|
||||
# Code generation
|
||||
#
|
||||
|
||||
class BufferEntry(object):
|
||||
def __init__(self, entry):
|
||||
self.entry = entry
|
||||
self.type = entry.type
|
||||
self.cname = entry.buffer_aux.buflocal_nd_var.cname
|
||||
self.buf_ptr = "%s.rcbuffer->pybuffer.buf" % self.cname
|
||||
self.buf_ptr_type = entry.type.buffer_ptr_type
|
||||
self.init_attributes()
|
||||
|
||||
def init_attributes(self):
|
||||
self.shape = self.get_buf_shapevars()
|
||||
self.strides = self.get_buf_stridevars()
|
||||
self.suboffsets = self.get_buf_suboffsetvars()
|
||||
|
||||
def get_buf_suboffsetvars(self):
|
||||
return self._for_all_ndim("%s.diminfo[%d].suboffsets")
|
||||
|
||||
def get_buf_stridevars(self):
|
||||
return self._for_all_ndim("%s.diminfo[%d].strides")
|
||||
|
||||
def get_buf_shapevars(self):
|
||||
return self._for_all_ndim("%s.diminfo[%d].shape")
|
||||
|
||||
def _for_all_ndim(self, s):
|
||||
return [s % (self.cname, i) for i in range(self.type.ndim)]
|
||||
|
||||
def generate_buffer_lookup_code(self, code, index_cnames):
|
||||
# Create buffer lookup and return it
|
||||
# This is done via utility macros/inline functions, which vary
|
||||
# according to the access mode used.
|
||||
params = []
|
||||
nd = self.type.ndim
|
||||
mode = self.type.mode
|
||||
if mode == 'full':
|
||||
for i, s, o in zip(index_cnames,
|
||||
self.get_buf_stridevars(),
|
||||
self.get_buf_suboffsetvars()):
|
||||
params.append(i)
|
||||
params.append(s)
|
||||
params.append(o)
|
||||
funcname = "__Pyx_BufPtrFull%dd" % nd
|
||||
funcgen = buf_lookup_full_code
|
||||
else:
|
||||
if mode == 'strided':
|
||||
funcname = "__Pyx_BufPtrStrided%dd" % nd
|
||||
funcgen = buf_lookup_strided_code
|
||||
elif mode == 'c':
|
||||
funcname = "__Pyx_BufPtrCContig%dd" % nd
|
||||
funcgen = buf_lookup_c_code
|
||||
elif mode == 'fortran':
|
||||
funcname = "__Pyx_BufPtrFortranContig%dd" % nd
|
||||
funcgen = buf_lookup_fortran_code
|
||||
else:
|
||||
assert False
|
||||
for i, s in zip(index_cnames, self.get_buf_stridevars()):
|
||||
params.append(i)
|
||||
params.append(s)
|
||||
|
||||
# Make sure the utility code is available
|
||||
if funcname not in code.globalstate.utility_codes:
|
||||
code.globalstate.utility_codes.add(funcname)
|
||||
protocode = code.globalstate['utility_code_proto']
|
||||
defcode = code.globalstate['utility_code_def']
|
||||
funcgen(protocode, defcode, name=funcname, nd=nd)
|
||||
|
||||
buf_ptr_type_code = self.buf_ptr_type.empty_declaration_code()
|
||||
ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr,
|
||||
", ".join(params))
|
||||
return ptrcode
|
||||
|
||||
|
||||
def get_flags(buffer_aux, buffer_type):
|
||||
flags = 'PyBUF_FORMAT'
|
||||
mode = buffer_type.mode
|
||||
if mode == 'full':
|
||||
flags += '| PyBUF_INDIRECT'
|
||||
elif mode == 'strided':
|
||||
flags += '| PyBUF_STRIDES'
|
||||
elif mode == 'c':
|
||||
flags += '| PyBUF_C_CONTIGUOUS'
|
||||
elif mode == 'fortran':
|
||||
flags += '| PyBUF_F_CONTIGUOUS'
|
||||
else:
|
||||
assert False
|
||||
if buffer_aux.writable_needed: flags += "| PyBUF_WRITABLE"
|
||||
return flags
|
||||
|
||||
def used_buffer_aux_vars(entry):
|
||||
buffer_aux = entry.buffer_aux
|
||||
buffer_aux.buflocal_nd_var.used = True
|
||||
buffer_aux.rcbuf_var.used = True
|
||||
|
||||
def put_unpack_buffer_aux_into_scope(buf_entry, code):
|
||||
# Generate code to copy the needed struct info into local
|
||||
# variables.
|
||||
buffer_aux, mode = buf_entry.buffer_aux, buf_entry.type.mode
|
||||
pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
|
||||
|
||||
fldnames = ['strides', 'shape']
|
||||
if mode == 'full':
|
||||
fldnames.append('suboffsets')
|
||||
|
||||
ln = []
|
||||
for i in range(buf_entry.type.ndim):
|
||||
for fldname in fldnames:
|
||||
ln.append("%s.diminfo[%d].%s = %s.rcbuffer->pybuffer.%s[%d];" % \
|
||||
(pybuffernd_struct, i, fldname,
|
||||
pybuffernd_struct, fldname, i))
|
||||
code.putln(' '.join(ln))
|
||||
|
||||
def put_init_vars(entry, code):
|
||||
bufaux = entry.buffer_aux
|
||||
pybuffernd_struct = bufaux.buflocal_nd_var.cname
|
||||
pybuffer_struct = bufaux.rcbuf_var.cname
|
||||
# init pybuffer_struct
|
||||
code.putln("%s.pybuffer.buf = NULL;" % pybuffer_struct)
|
||||
code.putln("%s.refcount = 0;" % pybuffer_struct)
|
||||
# init the buffer object
|
||||
# code.put_init_var_to_py_none(entry)
|
||||
# init the pybuffernd_struct
|
||||
code.putln("%s.data = NULL;" % pybuffernd_struct)
|
||||
code.putln("%s.rcbuffer = &%s;" % (pybuffernd_struct, pybuffer_struct))
|
||||
|
||||
|
||||
def put_acquire_arg_buffer(entry, code, pos):
|
||||
buffer_aux = entry.buffer_aux
|
||||
getbuffer = get_getbuffer_call(code, entry.cname, buffer_aux, entry.type)
|
||||
|
||||
# Acquire any new buffer
|
||||
code.putln("{")
|
||||
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % entry.type.dtype.struct_nesting_depth())
|
||||
code.putln(code.error_goto_if("%s == -1" % getbuffer, pos))
|
||||
code.putln("}")
|
||||
# An exception raised in arg parsing cannot be caught, so no
|
||||
# need to care about the buffer then.
|
||||
put_unpack_buffer_aux_into_scope(entry, code)
|
||||
|
||||
|
||||
def put_release_buffer_code(code, entry):
|
||||
code.globalstate.use_utility_code(acquire_utility_code)
|
||||
code.putln("__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);" % entry.buffer_aux.buflocal_nd_var.cname)
|
||||
|
||||
|
||||
def get_getbuffer_call(code, obj_cname, buffer_aux, buffer_type):
|
||||
ndim = buffer_type.ndim
|
||||
cast = int(buffer_type.cast)
|
||||
flags = get_flags(buffer_aux, buffer_type)
|
||||
pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
|
||||
|
||||
dtype_typeinfo = get_type_information_cname(code, buffer_type.dtype)
|
||||
|
||||
code.globalstate.use_utility_code(acquire_utility_code)
|
||||
return ("__Pyx_GetBufferAndValidate(&%(pybuffernd_struct)s.rcbuffer->pybuffer, "
|
||||
"(PyObject*)%(obj_cname)s, &%(dtype_typeinfo)s, %(flags)s, %(ndim)d, "
|
||||
"%(cast)d, __pyx_stack)" % locals())
|
||||
|
||||
|
||||
def put_assign_to_buffer(lhs_cname, rhs_cname, buf_entry,
|
||||
is_initialized, pos, code):
|
||||
"""
|
||||
Generate code for reassigning a buffer variables. This only deals with getting
|
||||
the buffer auxiliary structure and variables set up correctly, the assignment
|
||||
itself and refcounting is the responsibility of the caller.
|
||||
|
||||
However, the assignment operation may throw an exception so that the reassignment
|
||||
never happens.
|
||||
|
||||
Depending on the circumstances there are two possible outcomes:
|
||||
- Old buffer released, new acquired, rhs assigned to lhs
|
||||
- Old buffer released, new acquired which fails, reaqcuire old lhs buffer
|
||||
(which may or may not succeed).
|
||||
"""
|
||||
|
||||
buffer_aux, buffer_type = buf_entry.buffer_aux, buf_entry.type
|
||||
pybuffernd_struct = buffer_aux.buflocal_nd_var.cname
|
||||
flags = get_flags(buffer_aux, buffer_type)
|
||||
|
||||
code.putln("{") # Set up necessary stack for getbuffer
|
||||
code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" % buffer_type.dtype.struct_nesting_depth())
|
||||
|
||||
getbuffer = get_getbuffer_call(code, "%s", buffer_aux, buffer_type) # fill in object below
|
||||
|
||||
if is_initialized:
|
||||
# Release any existing buffer
|
||||
code.putln('__Pyx_SafeReleaseBuffer(&%s.rcbuffer->pybuffer);' % pybuffernd_struct)
|
||||
# Acquire
|
||||
retcode_cname = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
|
||||
code.putln("%s = %s;" % (retcode_cname, getbuffer % rhs_cname))
|
||||
code.putln('if (%s) {' % (code.unlikely("%s < 0" % retcode_cname)))
|
||||
# If acquisition failed, attempt to reacquire the old buffer
|
||||
# before raising the exception. A failure of reacquisition
|
||||
# will cause the reacquisition exception to be reported, one
|
||||
# can consider working around this later.
|
||||
exc_temps = tuple(code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=False)
|
||||
for _ in range(3))
|
||||
code.putln('PyErr_Fetch(&%s, &%s, &%s);' % exc_temps)
|
||||
code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % lhs_cname)))
|
||||
code.putln('Py_XDECREF(%s); Py_XDECREF(%s); Py_XDECREF(%s);' % exc_temps) # Do not refnanny these!
|
||||
code.globalstate.use_utility_code(raise_buffer_fallback_code)
|
||||
code.putln('__Pyx_RaiseBufferFallbackError();')
|
||||
code.putln('} else {')
|
||||
code.putln('PyErr_Restore(%s, %s, %s);' % exc_temps)
|
||||
code.putln('}')
|
||||
code.putln('%s = %s = %s = 0;' % exc_temps)
|
||||
for t in exc_temps:
|
||||
code.funcstate.release_temp(t)
|
||||
code.putln('}')
|
||||
# Unpack indices
|
||||
put_unpack_buffer_aux_into_scope(buf_entry, code)
|
||||
code.putln(code.error_goto_if_neg(retcode_cname, pos))
|
||||
code.funcstate.release_temp(retcode_cname)
|
||||
else:
|
||||
# Our entry had no previous value, so set to None when acquisition fails.
|
||||
# In this case, auxiliary vars should be set up right in initialization to a zero-buffer,
|
||||
# so it suffices to set the buf field to NULL.
|
||||
code.putln('if (%s) {' % code.unlikely("%s == -1" % (getbuffer % rhs_cname)))
|
||||
code.putln('%s = %s; __Pyx_INCREF(Py_None); %s.rcbuffer->pybuffer.buf = NULL;' %
|
||||
(lhs_cname,
|
||||
PyrexTypes.typecast(buffer_type, PyrexTypes.py_object_type, "Py_None"),
|
||||
pybuffernd_struct))
|
||||
code.putln(code.error_goto(pos))
|
||||
code.put('} else {')
|
||||
# Unpack indices
|
||||
put_unpack_buffer_aux_into_scope(buf_entry, code)
|
||||
code.putln('}')
|
||||
|
||||
code.putln("}") # Release stack
|
||||
|
||||
|
||||
def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
|
||||
pos, code, negative_indices, in_nogil_context):
|
||||
"""
|
||||
Generates code to process indices and calculate an offset into
|
||||
a buffer. Returns a C string which gives a pointer which can be
|
||||
read from or written to at will (it is an expression so caller should
|
||||
store it in a temporary if it is used more than once).
|
||||
|
||||
As the bounds checking can have any number of combinations of unsigned
|
||||
arguments, smart optimizations etc. we insert it directly in the function
|
||||
body. The lookup however is delegated to a inline function that is instantiated
|
||||
once per ndim (lookup with suboffsets tend to get quite complicated).
|
||||
|
||||
entry is a BufferEntry
|
||||
"""
|
||||
negative_indices = directives['wraparound'] and negative_indices
|
||||
|
||||
if directives['boundscheck']:
|
||||
# Check bounds and fix negative indices.
|
||||
# We allocate a temporary which is initialized to -1, meaning OK (!).
|
||||
# If an error occurs, the temp is set to the index dimension the
|
||||
# error is occurring at.
|
||||
failed_dim_temp = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
|
||||
code.putln("%s = -1;" % failed_dim_temp)
|
||||
for dim, (signed, cname, shape) in enumerate(zip(index_signeds, index_cnames, entry.get_buf_shapevars())):
|
||||
if signed != 0:
|
||||
# not unsigned, deal with negative index
|
||||
code.putln("if (%s < 0) {" % cname)
|
||||
if negative_indices:
|
||||
code.putln("%s += %s;" % (cname, shape))
|
||||
code.putln("if (%s) %s = %d;" % (
|
||||
code.unlikely("%s < 0" % cname),
|
||||
failed_dim_temp, dim))
|
||||
else:
|
||||
code.putln("%s = %d;" % (failed_dim_temp, dim))
|
||||
code.put("} else ")
|
||||
# check bounds in positive direction
|
||||
if signed != 0:
|
||||
cast = ""
|
||||
else:
|
||||
cast = "(size_t)"
|
||||
code.putln("if (%s) %s = %d;" % (
|
||||
code.unlikely("%s >= %s%s" % (cname, cast, shape)),
|
||||
failed_dim_temp, dim))
|
||||
|
||||
if in_nogil_context:
|
||||
code.globalstate.use_utility_code(raise_indexerror_nogil)
|
||||
func = '__Pyx_RaiseBufferIndexErrorNogil'
|
||||
else:
|
||||
code.globalstate.use_utility_code(raise_indexerror_code)
|
||||
func = '__Pyx_RaiseBufferIndexError'
|
||||
|
||||
code.putln("if (%s) {" % code.unlikely("%s != -1" % failed_dim_temp))
|
||||
code.putln('%s(%s);' % (func, failed_dim_temp))
|
||||
code.putln(code.error_goto(pos))
|
||||
code.putln('}')
|
||||
code.funcstate.release_temp(failed_dim_temp)
|
||||
elif negative_indices:
|
||||
# Only fix negative indices.
|
||||
for signed, cname, shape in zip(index_signeds, index_cnames, entry.get_buf_shapevars()):
|
||||
if signed != 0:
|
||||
code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape))
|
||||
|
||||
return entry.generate_buffer_lookup_code(code, index_cnames)
|
||||
|
||||
|
||||
def use_bufstruct_declare_code(env):
|
||||
env.use_utility_code(buffer_struct_declare_code)
|
||||
|
||||
|
||||
def buf_lookup_full_code(proto, defin, name, nd):
|
||||
"""
|
||||
Generates a buffer lookup function for the right number
|
||||
of dimensions. The function gives back a void* at the right location.
|
||||
"""
|
||||
# _i_ndex, _s_tride, sub_o_ffset
|
||||
macroargs = ", ".join(["i%d, s%d, o%d" % (i, i, i) for i in range(nd)])
|
||||
proto.putln("#define %s(type, buf, %s) (type)(%s_imp(buf, %s))" % (name, macroargs, name, macroargs))
|
||||
|
||||
funcargs = ", ".join(["Py_ssize_t i%d, Py_ssize_t s%d, Py_ssize_t o%d" % (i, i, i) for i in range(nd)])
|
||||
proto.putln("static CYTHON_INLINE void* %s_imp(void* buf, %s);" % (name, funcargs))
|
||||
defin.putln(dedent("""
|
||||
static CYTHON_INLINE void* %s_imp(void* buf, %s) {
|
||||
char* ptr = (char*)buf;
|
||||
""") % (name, funcargs) + "".join([dedent("""\
|
||||
ptr += s%d * i%d;
|
||||
if (o%d >= 0) ptr = *((char**)ptr) + o%d;
|
||||
""") % (i, i, i, i) for i in range(nd)]
|
||||
) + "\nreturn ptr;\n}")
|
||||
|
||||
|
||||
def buf_lookup_strided_code(proto, defin, name, nd):
|
||||
"""
|
||||
Generates a buffer lookup function for the right number
|
||||
of dimensions. The function gives back a void* at the right location.
|
||||
"""
|
||||
# _i_ndex, _s_tride
|
||||
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
|
||||
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd)])
|
||||
proto.putln("#define %s(type, buf, %s) (type)((char*)buf + %s)" % (name, args, offset))
|
||||
|
||||
|
||||
def buf_lookup_c_code(proto, defin, name, nd):
|
||||
"""
|
||||
Similar to strided lookup, but can assume that the last dimension
|
||||
doesn't need a multiplication as long as.
|
||||
Still we keep the same signature for now.
|
||||
"""
|
||||
if nd == 1:
|
||||
proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
|
||||
else:
|
||||
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
|
||||
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(nd - 1)])
|
||||
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, nd - 1))
|
||||
|
||||
|
||||
def buf_lookup_fortran_code(proto, defin, name, nd):
|
||||
"""
|
||||
Like C lookup, but the first index is optimized instead.
|
||||
"""
|
||||
if nd == 1:
|
||||
proto.putln("#define %s(type, buf, i0, s0) ((type)buf + i0)" % name)
|
||||
else:
|
||||
args = ", ".join(["i%d, s%d" % (i, i) for i in range(nd)])
|
||||
offset = " + ".join(["i%d * s%d" % (i, i) for i in range(1, nd)])
|
||||
proto.putln("#define %s(type, buf, %s) ((type)((char*)buf + %s) + i%d)" % (name, args, offset, 0))
|
||||
|
||||
|
||||
def use_py2_buffer_functions(env):
|
||||
env.use_utility_code(GetAndReleaseBufferUtilityCode())
|
||||
|
||||
|
||||
class GetAndReleaseBufferUtilityCode(object):
|
||||
# Emulation of PyObject_GetBuffer and PyBuffer_Release for Python 2.
|
||||
# For >= 2.6 we do double mode -- use the new buffer interface on objects
|
||||
# which has the right tp_flags set, but emulation otherwise.
|
||||
|
||||
requires = None
|
||||
is_cython_utility = False
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, GetAndReleaseBufferUtilityCode)
|
||||
|
||||
def __hash__(self):
|
||||
return 24342342
|
||||
|
||||
def get_tree(self, **kwargs): pass
|
||||
|
||||
def put_code(self, output):
|
||||
code = output['utility_code_def']
|
||||
proto_code = output['utility_code_proto']
|
||||
env = output.module_node.scope
|
||||
cython_scope = env.context.cython_scope
|
||||
|
||||
# Search all types for __getbuffer__ overloads
|
||||
types = []
|
||||
visited_scopes = set()
|
||||
def find_buffer_types(scope):
|
||||
if scope in visited_scopes:
|
||||
return
|
||||
visited_scopes.add(scope)
|
||||
for m in scope.cimported_modules:
|
||||
find_buffer_types(m)
|
||||
for e in scope.type_entries:
|
||||
if isinstance(e.utility_code_definition, CythonUtilityCode):
|
||||
continue
|
||||
t = e.type
|
||||
if t.is_extension_type:
|
||||
if scope is cython_scope and not e.used:
|
||||
continue
|
||||
release = get = None
|
||||
for x in t.scope.pyfunc_entries:
|
||||
if x.name == u"__getbuffer__": get = x.func_cname
|
||||
elif x.name == u"__releasebuffer__": release = x.func_cname
|
||||
if get:
|
||||
types.append((t.typeptr_cname, get, release))
|
||||
|
||||
find_buffer_types(env)
|
||||
|
||||
util_code = TempitaUtilityCode.load(
|
||||
"GetAndReleaseBuffer", from_file="Buffer.c",
|
||||
context=dict(types=types))
|
||||
|
||||
proto = util_code.format_code(util_code.proto)
|
||||
impl = util_code.format_code(
|
||||
util_code.inject_string_constants(util_code.impl, output)[1])
|
||||
|
||||
proto_code.putln(proto)
|
||||
code.putln(impl)
|
||||
|
||||
|
||||
def mangle_dtype_name(dtype):
|
||||
# Use prefixes to separate user defined types from builtins
|
||||
# (consider "typedef float unsigned_int")
|
||||
if dtype.is_pyobject:
|
||||
return "object"
|
||||
elif dtype.is_ptr:
|
||||
return "ptr"
|
||||
else:
|
||||
if dtype.is_typedef or dtype.is_struct_or_union:
|
||||
prefix = "nn_"
|
||||
else:
|
||||
prefix = ""
|
||||
return prefix + dtype.specialization_name()
|
||||
|
||||
def get_type_information_cname(code, dtype, maxdepth=None):
|
||||
"""
|
||||
Output the run-time type information (__Pyx_TypeInfo) for given dtype,
|
||||
and return the name of the type info struct.
|
||||
|
||||
Structs with two floats of the same size are encoded as complex numbers.
|
||||
One can separate between complex numbers declared as struct or with native
|
||||
encoding by inspecting to see if the fields field of the type is
|
||||
filled in.
|
||||
"""
|
||||
namesuffix = mangle_dtype_name(dtype)
|
||||
name = "__Pyx_TypeInfo_%s" % namesuffix
|
||||
structinfo_name = "__Pyx_StructFields_%s" % namesuffix
|
||||
|
||||
if dtype.is_error: return "<error>"
|
||||
|
||||
# It's critical that walking the type info doesn't use more stack
|
||||
# depth than dtype.struct_nesting_depth() returns, so use an assertion for this
|
||||
if maxdepth is None: maxdepth = dtype.struct_nesting_depth()
|
||||
if maxdepth <= 0:
|
||||
assert False
|
||||
|
||||
if name not in code.globalstate.utility_codes:
|
||||
code.globalstate.utility_codes.add(name)
|
||||
typecode = code.globalstate['typeinfo']
|
||||
|
||||
arraysizes = []
|
||||
if dtype.is_array:
|
||||
while dtype.is_array:
|
||||
arraysizes.append(dtype.size)
|
||||
dtype = dtype.base_type
|
||||
|
||||
complex_possible = dtype.is_struct_or_union and dtype.can_be_complex()
|
||||
|
||||
declcode = dtype.empty_declaration_code()
|
||||
if dtype.is_simple_buffer_dtype():
|
||||
structinfo_name = "NULL"
|
||||
elif dtype.is_struct:
|
||||
struct_scope = dtype.scope
|
||||
if dtype.is_const:
|
||||
struct_scope = struct_scope.const_base_type_scope
|
||||
# Must pre-call all used types in order not to recurse during utility code writing.
|
||||
fields = struct_scope.var_entries
|
||||
assert len(fields) > 0
|
||||
types = [get_type_information_cname(code, f.type, maxdepth - 1)
|
||||
for f in fields]
|
||||
typecode.putln("static __Pyx_StructField %s[] = {" % structinfo_name, safe=True)
|
||||
for f, typeinfo in zip(fields, types):
|
||||
typecode.putln(' {&%s, "%s", offsetof(%s, %s)},' %
|
||||
(typeinfo, f.name, dtype.empty_declaration_code(), f.cname), safe=True)
|
||||
typecode.putln(' {NULL, NULL, 0}', safe=True)
|
||||
typecode.putln("};", safe=True)
|
||||
else:
|
||||
assert False
|
||||
|
||||
rep = str(dtype)
|
||||
|
||||
flags = "0"
|
||||
is_unsigned = "0"
|
||||
if dtype is PyrexTypes.c_char_type:
|
||||
is_unsigned = "IS_UNSIGNED(%s)" % declcode
|
||||
typegroup = "'H'"
|
||||
elif dtype.is_int:
|
||||
is_unsigned = "IS_UNSIGNED(%s)" % declcode
|
||||
typegroup = "%s ? 'U' : 'I'" % is_unsigned
|
||||
elif complex_possible or dtype.is_complex:
|
||||
typegroup = "'C'"
|
||||
elif dtype.is_float:
|
||||
typegroup = "'R'"
|
||||
elif dtype.is_struct:
|
||||
typegroup = "'S'"
|
||||
if dtype.packed:
|
||||
flags = "__PYX_BUF_FLAGS_PACKED_STRUCT"
|
||||
elif dtype.is_pyobject:
|
||||
typegroup = "'O'"
|
||||
else:
|
||||
assert False, dtype
|
||||
|
||||
typeinfo = ('static __Pyx_TypeInfo %s = '
|
||||
'{ "%s", %s, sizeof(%s), { %s }, %s, %s, %s, %s };')
|
||||
tup = (name, rep, structinfo_name, declcode,
|
||||
', '.join([str(x) for x in arraysizes]) or '0', len(arraysizes),
|
||||
typegroup, is_unsigned, flags)
|
||||
typecode.putln(typeinfo % tup, safe=True)
|
||||
|
||||
return name
|
||||
|
||||
def load_buffer_utility(util_code_name, context=None, **kwargs):
|
||||
if context is None:
|
||||
return UtilityCode.load(util_code_name, "Buffer.c", **kwargs)
|
||||
else:
|
||||
return TempitaUtilityCode.load(util_code_name, "Buffer.c", context=context, **kwargs)
|
||||
|
||||
context = dict(max_dims=Options.buffer_max_dims)
|
||||
buffer_struct_declare_code = load_buffer_utility("BufferStructDeclare", context=context)
|
||||
buffer_formats_declare_code = load_buffer_utility("BufferFormatStructs")
|
||||
|
||||
# Utility function to set the right exception
|
||||
# The caller should immediately goto_error
|
||||
raise_indexerror_code = load_buffer_utility("BufferIndexError")
|
||||
raise_indexerror_nogil = load_buffer_utility("BufferIndexErrorNogil")
|
||||
raise_buffer_fallback_code = load_buffer_utility("BufferFallbackError")
|
||||
|
||||
acquire_utility_code = load_buffer_utility("BufferGetAndValidate", context=context)
|
||||
buffer_format_check_code = load_buffer_utility("BufferFormatCheck", context=context)
|
||||
|
||||
# See utility code BufferFormatFromTypeInfo
|
||||
_typeinfo_to_format_code = load_buffer_utility("TypeInfoToFormat")
|
||||
@@ -0,0 +1,444 @@
|
||||
#
|
||||
# Builtin Definitions
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Symtab import BuiltinScope, StructOrUnionScope
|
||||
from .Code import UtilityCode
|
||||
from .TypeSlots import Signature
|
||||
from . import PyrexTypes
|
||||
from . import Options
|
||||
|
||||
|
||||
# C-level implementations of builtin types, functions and methods
|
||||
|
||||
iter_next_utility_code = UtilityCode.load("IterNext", "ObjectHandling.c")
|
||||
getattr_utility_code = UtilityCode.load("GetAttr", "ObjectHandling.c")
|
||||
getattr3_utility_code = UtilityCode.load("GetAttr3", "Builtins.c")
|
||||
pyexec_utility_code = UtilityCode.load("PyExec", "Builtins.c")
|
||||
pyexec_globals_utility_code = UtilityCode.load("PyExecGlobals", "Builtins.c")
|
||||
globals_utility_code = UtilityCode.load("Globals", "Builtins.c")
|
||||
|
||||
builtin_utility_code = {
|
||||
'StopAsyncIteration': UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c"),
|
||||
}
|
||||
|
||||
|
||||
# mapping from builtins to their C-level equivalents
|
||||
|
||||
class _BuiltinOverride(object):
|
||||
def __init__(self, py_name, args, ret_type, cname, py_equiv="*",
|
||||
utility_code=None, sig=None, func_type=None,
|
||||
is_strict_signature=False, builtin_return_type=None):
|
||||
self.py_name, self.cname, self.py_equiv = py_name, cname, py_equiv
|
||||
self.args, self.ret_type = args, ret_type
|
||||
self.func_type, self.sig = func_type, sig
|
||||
self.builtin_return_type = builtin_return_type
|
||||
self.is_strict_signature = is_strict_signature
|
||||
self.utility_code = utility_code
|
||||
|
||||
def build_func_type(self, sig=None, self_arg=None):
|
||||
if sig is None:
|
||||
sig = Signature(self.args, self.ret_type)
|
||||
sig.exception_check = False # not needed for the current builtins
|
||||
func_type = sig.function_type(self_arg)
|
||||
if self.is_strict_signature:
|
||||
func_type.is_strict_signature = True
|
||||
if self.builtin_return_type:
|
||||
func_type.return_type = builtin_types[self.builtin_return_type]
|
||||
return func_type
|
||||
|
||||
|
||||
class BuiltinAttribute(object):
|
||||
def __init__(self, py_name, cname=None, field_type=None, field_type_name=None):
|
||||
self.py_name = py_name
|
||||
self.cname = cname or py_name
|
||||
self.field_type_name = field_type_name # can't do the lookup before the type is declared!
|
||||
self.field_type = field_type
|
||||
|
||||
def declare_in_type(self, self_type):
|
||||
if self.field_type_name is not None:
|
||||
# lazy type lookup
|
||||
field_type = builtin_scope.lookup(self.field_type_name).type
|
||||
else:
|
||||
field_type = self.field_type or PyrexTypes.py_object_type
|
||||
entry = self_type.scope.declare(self.py_name, self.cname, field_type, None, 'private')
|
||||
entry.is_variable = True
|
||||
|
||||
|
||||
class BuiltinFunction(_BuiltinOverride):
|
||||
def declare_in_scope(self, scope):
|
||||
func_type, sig = self.func_type, self.sig
|
||||
if func_type is None:
|
||||
func_type = self.build_func_type(sig)
|
||||
scope.declare_builtin_cfunction(self.py_name, func_type, self.cname,
|
||||
self.py_equiv, self.utility_code)
|
||||
|
||||
|
||||
class BuiltinMethod(_BuiltinOverride):
|
||||
def declare_in_type(self, self_type):
|
||||
method_type, sig = self.func_type, self.sig
|
||||
if method_type is None:
|
||||
# override 'self' type (first argument)
|
||||
self_arg = PyrexTypes.CFuncTypeArg("", self_type, None)
|
||||
self_arg.not_none = True
|
||||
self_arg.accept_builtin_subtypes = True
|
||||
method_type = self.build_func_type(sig, self_arg)
|
||||
self_type.scope.declare_builtin_cfunction(
|
||||
self.py_name, method_type, self.cname, utility_code=self.utility_code)
|
||||
|
||||
|
||||
builtin_function_table = [
|
||||
# name, args, return, C API func, py equiv = "*"
|
||||
BuiltinFunction('abs', "d", "d", "fabs",
|
||||
is_strict_signature = True),
|
||||
BuiltinFunction('abs', "f", "f", "fabsf",
|
||||
is_strict_signature = True),
|
||||
BuiltinFunction('abs', "i", "i", "abs",
|
||||
is_strict_signature = True),
|
||||
BuiltinFunction('abs', "l", "l", "labs",
|
||||
is_strict_signature = True),
|
||||
BuiltinFunction('abs', None, None, "__Pyx_abs_longlong",
|
||||
utility_code = UtilityCode.load("abs_longlong", "Builtins.c"),
|
||||
func_type = PyrexTypes.CFuncType(
|
||||
PyrexTypes.c_longlong_type, [
|
||||
PyrexTypes.CFuncTypeArg("arg", PyrexTypes.c_longlong_type, None)
|
||||
],
|
||||
is_strict_signature = True, nogil=True)),
|
||||
] + list(
|
||||
BuiltinFunction('abs', None, None, "/*abs_{0}*/".format(t.specialization_name()),
|
||||
func_type = PyrexTypes.CFuncType(
|
||||
t,
|
||||
[PyrexTypes.CFuncTypeArg("arg", t, None)],
|
||||
is_strict_signature = True, nogil=True))
|
||||
for t in (PyrexTypes.c_uint_type, PyrexTypes.c_ulong_type, PyrexTypes.c_ulonglong_type)
|
||||
) + list(
|
||||
BuiltinFunction('abs', None, None, "__Pyx_c_abs{0}".format(t.funcsuffix),
|
||||
func_type = PyrexTypes.CFuncType(
|
||||
t.real_type, [
|
||||
PyrexTypes.CFuncTypeArg("arg", t, None)
|
||||
],
|
||||
is_strict_signature = True, nogil=True))
|
||||
for t in (PyrexTypes.c_float_complex_type,
|
||||
PyrexTypes.c_double_complex_type,
|
||||
PyrexTypes.c_longdouble_complex_type)
|
||||
) + [
|
||||
BuiltinFunction('abs', "O", "O", "__Pyx_PyNumber_Absolute",
|
||||
utility_code=UtilityCode.load("py_abs", "Builtins.c")),
|
||||
#('all', "", "", ""),
|
||||
#('any', "", "", ""),
|
||||
#('ascii', "", "", ""),
|
||||
#('bin', "", "", ""),
|
||||
BuiltinFunction('callable', "O", "b", "__Pyx_PyCallable_Check",
|
||||
utility_code = UtilityCode.load("CallableCheck", "ObjectHandling.c")),
|
||||
#('chr', "", "", ""),
|
||||
#('cmp', "", "", "", ""), # int PyObject_Cmp(PyObject *o1, PyObject *o2, int *result)
|
||||
#('compile', "", "", ""), # PyObject* Py_CompileString( char *str, char *filename, int start)
|
||||
BuiltinFunction('delattr', "OO", "r", "PyObject_DelAttr"),
|
||||
BuiltinFunction('dir', "O", "O", "PyObject_Dir"),
|
||||
BuiltinFunction('divmod', "OO", "O", "PyNumber_Divmod"),
|
||||
BuiltinFunction('exec', "O", "O", "__Pyx_PyExecGlobals",
|
||||
utility_code = pyexec_globals_utility_code),
|
||||
BuiltinFunction('exec', "OO", "O", "__Pyx_PyExec2",
|
||||
utility_code = pyexec_utility_code),
|
||||
BuiltinFunction('exec', "OOO", "O", "__Pyx_PyExec3",
|
||||
utility_code = pyexec_utility_code),
|
||||
#('eval', "", "", ""),
|
||||
#('execfile', "", "", ""),
|
||||
#('filter', "", "", ""),
|
||||
BuiltinFunction('getattr3', "OOO", "O", "__Pyx_GetAttr3", "getattr",
|
||||
utility_code=getattr3_utility_code), # Pyrex legacy
|
||||
BuiltinFunction('getattr', "OOO", "O", "__Pyx_GetAttr3",
|
||||
utility_code=getattr3_utility_code),
|
||||
BuiltinFunction('getattr', "OO", "O", "__Pyx_GetAttr",
|
||||
utility_code=getattr_utility_code),
|
||||
BuiltinFunction('hasattr', "OO", "b", "__Pyx_HasAttr",
|
||||
utility_code = UtilityCode.load("HasAttr", "Builtins.c")),
|
||||
BuiltinFunction('hash', "O", "h", "PyObject_Hash"),
|
||||
#('hex', "", "", ""),
|
||||
#('id', "", "", ""),
|
||||
#('input', "", "", ""),
|
||||
BuiltinFunction('intern', "O", "O", "__Pyx_Intern",
|
||||
utility_code = UtilityCode.load("Intern", "Builtins.c")),
|
||||
BuiltinFunction('isinstance', "OO", "b", "PyObject_IsInstance"),
|
||||
BuiltinFunction('issubclass', "OO", "b", "PyObject_IsSubclass"),
|
||||
BuiltinFunction('iter', "OO", "O", "PyCallIter_New"),
|
||||
BuiltinFunction('iter', "O", "O", "PyObject_GetIter"),
|
||||
BuiltinFunction('len', "O", "z", "PyObject_Length"),
|
||||
BuiltinFunction('locals', "", "O", "__pyx_locals"),
|
||||
#('map', "", "", ""),
|
||||
#('max', "", "", ""),
|
||||
#('min', "", "", ""),
|
||||
BuiltinFunction('next', "O", "O", "__Pyx_PyIter_Next",
|
||||
utility_code = iter_next_utility_code), # not available in Py2 => implemented here
|
||||
BuiltinFunction('next', "OO", "O", "__Pyx_PyIter_Next2",
|
||||
utility_code = iter_next_utility_code), # not available in Py2 => implemented here
|
||||
#('oct', "", "", ""),
|
||||
#('open', "ss", "O", "PyFile_FromString"), # not in Py3
|
||||
] + [
|
||||
BuiltinFunction('ord', None, None, "__Pyx_long_cast",
|
||||
func_type=PyrexTypes.CFuncType(
|
||||
PyrexTypes.c_long_type, [PyrexTypes.CFuncTypeArg("c", c_type, None)],
|
||||
is_strict_signature=True))
|
||||
for c_type in [PyrexTypes.c_py_ucs4_type, PyrexTypes.c_py_unicode_type]
|
||||
] + [
|
||||
BuiltinFunction('ord', None, None, "__Pyx_uchar_cast",
|
||||
func_type=PyrexTypes.CFuncType(
|
||||
PyrexTypes.c_uchar_type, [PyrexTypes.CFuncTypeArg("c", c_type, None)],
|
||||
is_strict_signature=True))
|
||||
for c_type in [PyrexTypes.c_char_type, PyrexTypes.c_schar_type, PyrexTypes.c_uchar_type]
|
||||
] + [
|
||||
BuiltinFunction('ord', None, None, "__Pyx_PyObject_Ord",
|
||||
utility_code=UtilityCode.load_cached("object_ord", "Builtins.c"),
|
||||
func_type=PyrexTypes.CFuncType(
|
||||
PyrexTypes.c_long_type, [
|
||||
PyrexTypes.CFuncTypeArg("c", PyrexTypes.py_object_type, None)
|
||||
],
|
||||
exception_value="(long)(Py_UCS4)-1")),
|
||||
BuiltinFunction('pow', "OOO", "O", "PyNumber_Power"),
|
||||
BuiltinFunction('pow', "OO", "O", "__Pyx_PyNumber_Power2",
|
||||
utility_code = UtilityCode.load("pow2", "Builtins.c")),
|
||||
#('range', "", "", ""),
|
||||
#('raw_input', "", "", ""),
|
||||
#('reduce', "", "", ""),
|
||||
BuiltinFunction('reload', "O", "O", "PyImport_ReloadModule"),
|
||||
BuiltinFunction('repr', "O", "O", "PyObject_Repr"), # , builtin_return_type='str'), # add in Cython 3.1
|
||||
#('round', "", "", ""),
|
||||
BuiltinFunction('setattr', "OOO", "r", "PyObject_SetAttr"),
|
||||
#('sum', "", "", ""),
|
||||
#('sorted', "", "", ""),
|
||||
#('type', "O", "O", "PyObject_Type"),
|
||||
#('unichr', "", "", ""),
|
||||
#('unicode', "", "", ""),
|
||||
#('vars', "", "", ""),
|
||||
#('zip', "", "", ""),
|
||||
# Can't do these easily until we have builtin type entries.
|
||||
#('typecheck', "OO", "i", "PyObject_TypeCheck", False),
|
||||
#('issubtype', "OO", "i", "PyType_IsSubtype", False),
|
||||
|
||||
# Put in namespace append optimization.
|
||||
BuiltinFunction('__Pyx_PyObject_Append', "OO", "O", "__Pyx_PyObject_Append"),
|
||||
|
||||
# This is conditionally looked up based on a compiler directive.
|
||||
BuiltinFunction('__Pyx_Globals', "", "O", "__Pyx_Globals",
|
||||
utility_code=globals_utility_code),
|
||||
]
|
||||
|
||||
|
||||
# Builtin types
|
||||
# bool
|
||||
# buffer
|
||||
# classmethod
|
||||
# dict
|
||||
# enumerate
|
||||
# file
|
||||
# float
|
||||
# int
|
||||
# list
|
||||
# long
|
||||
# object
|
||||
# property
|
||||
# slice
|
||||
# staticmethod
|
||||
# super
|
||||
# str
|
||||
# tuple
|
||||
# type
|
||||
# xrange
|
||||
|
||||
builtin_types_table = [
|
||||
|
||||
("type", "PyType_Type", []),
|
||||
|
||||
# This conflicts with the C++ bool type, and unfortunately
|
||||
# C++ is too liberal about PyObject* <-> bool conversions,
|
||||
# resulting in unintuitive runtime behavior and segfaults.
|
||||
# ("bool", "PyBool_Type", []),
|
||||
|
||||
("int", "PyInt_Type", []),
|
||||
("long", "PyLong_Type", []),
|
||||
("float", "PyFloat_Type", []),
|
||||
|
||||
("complex", "PyComplex_Type", [BuiltinAttribute('cval', field_type_name = 'Py_complex'),
|
||||
BuiltinAttribute('real', 'cval.real', field_type = PyrexTypes.c_double_type),
|
||||
BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
|
||||
]),
|
||||
|
||||
("basestring", "PyBaseString_Type", [
|
||||
BuiltinMethod("join", "TO", "T", "__Pyx_PyBaseString_Join",
|
||||
utility_code=UtilityCode.load("StringJoin", "StringTools.c")),
|
||||
]),
|
||||
("bytearray", "PyByteArray_Type", [
|
||||
]),
|
||||
("bytes", "PyBytes_Type", [BuiltinMethod("join", "TO", "O", "__Pyx_PyBytes_Join",
|
||||
utility_code=UtilityCode.load("StringJoin", "StringTools.c")),
|
||||
]),
|
||||
("str", "PyString_Type", [BuiltinMethod("join", "TO", "O", "__Pyx_PyString_Join",
|
||||
builtin_return_type='basestring',
|
||||
utility_code=UtilityCode.load("StringJoin", "StringTools.c")),
|
||||
]),
|
||||
("unicode", "PyUnicode_Type", [BuiltinMethod("__contains__", "TO", "b", "PyUnicode_Contains"),
|
||||
BuiltinMethod("join", "TO", "T", "PyUnicode_Join"),
|
||||
]),
|
||||
|
||||
("tuple", "PyTuple_Type", []),
|
||||
|
||||
("list", "PyList_Type", [BuiltinMethod("insert", "TzO", "r", "PyList_Insert"),
|
||||
BuiltinMethod("reverse", "T", "r", "PyList_Reverse"),
|
||||
BuiltinMethod("append", "TO", "r", "__Pyx_PyList_Append",
|
||||
utility_code=UtilityCode.load("ListAppend", "Optimize.c")),
|
||||
BuiltinMethod("extend", "TO", "r", "__Pyx_PyList_Extend",
|
||||
utility_code=UtilityCode.load("ListExtend", "Optimize.c")),
|
||||
]),
|
||||
|
||||
("dict", "PyDict_Type", [BuiltinMethod("__contains__", "TO", "b", "PyDict_Contains"),
|
||||
BuiltinMethod("has_key", "TO", "b", "PyDict_Contains"),
|
||||
BuiltinMethod("items", "T", "O", "__Pyx_PyDict_Items",
|
||||
utility_code=UtilityCode.load("py_dict_items", "Builtins.c")),
|
||||
BuiltinMethod("keys", "T", "O", "__Pyx_PyDict_Keys",
|
||||
utility_code=UtilityCode.load("py_dict_keys", "Builtins.c")),
|
||||
BuiltinMethod("values", "T", "O", "__Pyx_PyDict_Values",
|
||||
utility_code=UtilityCode.load("py_dict_values", "Builtins.c")),
|
||||
BuiltinMethod("iteritems", "T", "O", "__Pyx_PyDict_IterItems",
|
||||
utility_code=UtilityCode.load("py_dict_iteritems", "Builtins.c")),
|
||||
BuiltinMethod("iterkeys", "T", "O", "__Pyx_PyDict_IterKeys",
|
||||
utility_code=UtilityCode.load("py_dict_iterkeys", "Builtins.c")),
|
||||
BuiltinMethod("itervalues", "T", "O", "__Pyx_PyDict_IterValues",
|
||||
utility_code=UtilityCode.load("py_dict_itervalues", "Builtins.c")),
|
||||
BuiltinMethod("viewitems", "T", "O", "__Pyx_PyDict_ViewItems",
|
||||
utility_code=UtilityCode.load("py_dict_viewitems", "Builtins.c")),
|
||||
BuiltinMethod("viewkeys", "T", "O", "__Pyx_PyDict_ViewKeys",
|
||||
utility_code=UtilityCode.load("py_dict_viewkeys", "Builtins.c")),
|
||||
BuiltinMethod("viewvalues", "T", "O", "__Pyx_PyDict_ViewValues",
|
||||
utility_code=UtilityCode.load("py_dict_viewvalues", "Builtins.c")),
|
||||
BuiltinMethod("clear", "T", "r", "__Pyx_PyDict_Clear",
|
||||
utility_code=UtilityCode.load("py_dict_clear", "Optimize.c")),
|
||||
BuiltinMethod("copy", "T", "T", "PyDict_Copy")]),
|
||||
|
||||
("slice", "PySlice_Type", [BuiltinAttribute('start'),
|
||||
BuiltinAttribute('stop'),
|
||||
BuiltinAttribute('step'),
|
||||
]),
|
||||
# ("file", "PyFile_Type", []), # not in Py3
|
||||
|
||||
("set", "PySet_Type", [BuiltinMethod("clear", "T", "r", "PySet_Clear"),
|
||||
# discard() and remove() have a special treatment for unhashable values
|
||||
BuiltinMethod("discard", "TO", "r", "__Pyx_PySet_Discard",
|
||||
utility_code=UtilityCode.load("py_set_discard", "Optimize.c")),
|
||||
BuiltinMethod("remove", "TO", "r", "__Pyx_PySet_Remove",
|
||||
utility_code=UtilityCode.load("py_set_remove", "Optimize.c")),
|
||||
# update is actually variadic (see Github issue #1645)
|
||||
# BuiltinMethod("update", "TO", "r", "__Pyx_PySet_Update",
|
||||
# utility_code=UtilityCode.load_cached("PySet_Update", "Builtins.c")),
|
||||
BuiltinMethod("add", "TO", "r", "PySet_Add"),
|
||||
BuiltinMethod("pop", "T", "O", "PySet_Pop")]),
|
||||
("frozenset", "PyFrozenSet_Type", []),
|
||||
("Exception", "((PyTypeObject*)PyExc_Exception)[0]", []),
|
||||
("StopAsyncIteration", "((PyTypeObject*)__Pyx_PyExc_StopAsyncIteration)[0]", []),
|
||||
]
|
||||
|
||||
|
||||
types_that_construct_their_instance = set([
|
||||
# some builtin types do not always return an instance of
|
||||
# themselves - these do:
|
||||
'type', 'bool', 'long', 'float', 'complex',
|
||||
'bytes', 'unicode', 'bytearray',
|
||||
'tuple', 'list', 'dict', 'set', 'frozenset'
|
||||
# 'str', # only in Py3.x
|
||||
# 'file', # only in Py2.x
|
||||
])
|
||||
|
||||
|
||||
builtin_structs_table = [
|
||||
('Py_buffer', 'Py_buffer',
|
||||
[("buf", PyrexTypes.c_void_ptr_type),
|
||||
("obj", PyrexTypes.py_object_type),
|
||||
("len", PyrexTypes.c_py_ssize_t_type),
|
||||
("itemsize", PyrexTypes.c_py_ssize_t_type),
|
||||
("readonly", PyrexTypes.c_bint_type),
|
||||
("ndim", PyrexTypes.c_int_type),
|
||||
("format", PyrexTypes.c_char_ptr_type),
|
||||
("shape", PyrexTypes.c_py_ssize_t_ptr_type),
|
||||
("strides", PyrexTypes.c_py_ssize_t_ptr_type),
|
||||
("suboffsets", PyrexTypes.c_py_ssize_t_ptr_type),
|
||||
("smalltable", PyrexTypes.CArrayType(PyrexTypes.c_py_ssize_t_type, 2)),
|
||||
("internal", PyrexTypes.c_void_ptr_type),
|
||||
]),
|
||||
('Py_complex', 'Py_complex',
|
||||
[('real', PyrexTypes.c_double_type),
|
||||
('imag', PyrexTypes.c_double_type),
|
||||
])
|
||||
]
|
||||
|
||||
# set up builtin scope
|
||||
|
||||
builtin_scope = BuiltinScope()
|
||||
|
||||
def init_builtin_funcs():
|
||||
for bf in builtin_function_table:
|
||||
bf.declare_in_scope(builtin_scope)
|
||||
|
||||
builtin_types = {}
|
||||
|
||||
def init_builtin_types():
|
||||
global builtin_types
|
||||
for name, cname, methods in builtin_types_table:
|
||||
utility = builtin_utility_code.get(name)
|
||||
if name == 'frozenset':
|
||||
objstruct_cname = 'PySetObject'
|
||||
elif name == 'bytearray':
|
||||
objstruct_cname = 'PyByteArrayObject'
|
||||
elif name == 'bool':
|
||||
objstruct_cname = None
|
||||
elif name == 'Exception':
|
||||
objstruct_cname = "PyBaseExceptionObject"
|
||||
elif name == 'StopAsyncIteration':
|
||||
objstruct_cname = "PyBaseExceptionObject"
|
||||
else:
|
||||
objstruct_cname = 'Py%sObject' % name.capitalize()
|
||||
the_type = builtin_scope.declare_builtin_type(name, cname, utility, objstruct_cname)
|
||||
builtin_types[name] = the_type
|
||||
for method in methods:
|
||||
method.declare_in_type(the_type)
|
||||
|
||||
def init_builtin_structs():
|
||||
for name, cname, attribute_types in builtin_structs_table:
|
||||
scope = StructOrUnionScope(name)
|
||||
for attribute_name, attribute_type in attribute_types:
|
||||
scope.declare_var(attribute_name, attribute_type, None,
|
||||
attribute_name, allow_pyobject=True)
|
||||
builtin_scope.declare_struct_or_union(
|
||||
name, "struct", scope, 1, None, cname = cname)
|
||||
|
||||
|
||||
def init_builtins():
|
||||
init_builtin_structs()
|
||||
init_builtin_types()
|
||||
init_builtin_funcs()
|
||||
|
||||
builtin_scope.declare_var(
|
||||
'__debug__', PyrexTypes.c_const_type(PyrexTypes.c_bint_type),
|
||||
pos=None, cname='(!Py_OptimizeFlag)', is_cdef=True)
|
||||
|
||||
global list_type, tuple_type, dict_type, set_type, frozenset_type
|
||||
global bytes_type, str_type, unicode_type, basestring_type, slice_type
|
||||
global float_type, bool_type, type_type, complex_type, bytearray_type
|
||||
type_type = builtin_scope.lookup('type').type
|
||||
list_type = builtin_scope.lookup('list').type
|
||||
tuple_type = builtin_scope.lookup('tuple').type
|
||||
dict_type = builtin_scope.lookup('dict').type
|
||||
set_type = builtin_scope.lookup('set').type
|
||||
frozenset_type = builtin_scope.lookup('frozenset').type
|
||||
slice_type = builtin_scope.lookup('slice').type
|
||||
bytes_type = builtin_scope.lookup('bytes').type
|
||||
str_type = builtin_scope.lookup('str').type
|
||||
unicode_type = builtin_scope.lookup('unicode').type
|
||||
basestring_type = builtin_scope.lookup('basestring').type
|
||||
bytearray_type = builtin_scope.lookup('bytearray').type
|
||||
float_type = builtin_scope.lookup('float').type
|
||||
bool_type = builtin_scope.lookup('bool').type
|
||||
complex_type = builtin_scope.lookup('complex').type
|
||||
|
||||
|
||||
init_builtins()
|
||||
@@ -0,0 +1,239 @@
|
||||
#
|
||||
# Cython - Command Line Parsing
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
from . import Options
|
||||
|
||||
usage = """\
|
||||
Cython (http://cython.org) is a compiler for code written in the
|
||||
Cython language. Cython is based on Pyrex by Greg Ewing.
|
||||
|
||||
Usage: cython [options] sourcefile.{pyx,py} ...
|
||||
|
||||
Options:
|
||||
-V, --version Display version number of cython compiler
|
||||
-l, --create-listing Write error messages to a listing file
|
||||
-I, --include-dir <directory> Search for include files in named directory
|
||||
(multiple include directories are allowed).
|
||||
-o, --output-file <filename> Specify name of generated C file
|
||||
-t, --timestamps Only compile newer source files
|
||||
-f, --force Compile all source files (overrides implied -t)
|
||||
-v, --verbose Be verbose, print file names on multiple compilation
|
||||
-p, --embed-positions If specified, the positions in Cython files of each
|
||||
function definition is embedded in its docstring.
|
||||
--cleanup <level> Release interned objects on python exit, for memory debugging.
|
||||
Level indicates aggressiveness, default 0 releases nothing.
|
||||
-w, --working <directory> Sets the working directory for Cython (the directory modules
|
||||
are searched from)
|
||||
--gdb Output debug information for cygdb
|
||||
--gdb-outdir <directory> Specify gdb debug information output directory. Implies --gdb.
|
||||
|
||||
-D, --no-docstrings Strip docstrings from the compiled module.
|
||||
-a, --annotate Produce a colorized HTML version of the source.
|
||||
--annotate-coverage <cov.xml> Annotate and include coverage information from cov.xml.
|
||||
--line-directives Produce #line directives pointing to the .pyx source
|
||||
--cplus Output a C++ rather than C file.
|
||||
--embed[=<method_name>] Generate a main() function that embeds the Python interpreter.
|
||||
-2 Compile based on Python-2 syntax and code semantics.
|
||||
-3 Compile based on Python-3 syntax and code semantics.
|
||||
--3str Compile based on Python-3 syntax and code semantics without
|
||||
assuming unicode by default for string literals under Python 2.
|
||||
--lenient Change some compile time errors to runtime errors to
|
||||
improve Python compatibility
|
||||
--capi-reexport-cincludes Add cincluded headers to any auto-generated header files.
|
||||
--fast-fail Abort the compilation on the first error
|
||||
--warning-errors, -Werror Make all warnings into errors
|
||||
--warning-extra, -Wextra Enable extra warnings
|
||||
-X, --directive <name>=<value>[,<name=value,...] Overrides a compiler directive
|
||||
-E, --compile-time-env name=value[,<name=value,...] Provides compile time env like DEF would do.
|
||||
--module-name Fully qualified module name. If not given, it is deduced from the
|
||||
import path if source file is in a package, or equals the
|
||||
filename otherwise.
|
||||
"""
|
||||
|
||||
|
||||
# The following experimental options are supported only on MacOSX:
|
||||
# -C, --compile Compile generated .c file to .o file
|
||||
# --link Link .o file to produce extension module (implies -C)
|
||||
# -+, --cplus Use C++ compiler for compiling and linking
|
||||
# Additional .o files to link may be supplied when using -X."""
|
||||
|
||||
def bad_usage():
|
||||
sys.stderr.write(usage)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def parse_command_line(args):
|
||||
from .Main import CompilationOptions, default_options
|
||||
|
||||
pending_arg = []
|
||||
|
||||
def pop_arg():
|
||||
if not args or pending_arg:
|
||||
bad_usage()
|
||||
if '=' in args[0] and args[0].startswith('--'): # allow "--long-option=xyz"
|
||||
name, value = args.pop(0).split('=', 1)
|
||||
pending_arg.append(value)
|
||||
return name
|
||||
return args.pop(0)
|
||||
|
||||
def pop_value(default=None):
|
||||
if pending_arg:
|
||||
return pending_arg.pop()
|
||||
elif default is not None:
|
||||
return default
|
||||
elif not args:
|
||||
bad_usage()
|
||||
return args.pop(0)
|
||||
|
||||
def get_param(option):
|
||||
tail = option[2:]
|
||||
if tail:
|
||||
return tail
|
||||
else:
|
||||
return pop_arg()
|
||||
|
||||
options = CompilationOptions(default_options)
|
||||
sources = []
|
||||
while args:
|
||||
if args[0].startswith("-"):
|
||||
option = pop_arg()
|
||||
if option in ("-V", "--version"):
|
||||
options.show_version = 1
|
||||
elif option in ("-l", "--create-listing"):
|
||||
options.use_listing_file = 1
|
||||
elif option in ("-+", "--cplus"):
|
||||
options.cplus = 1
|
||||
elif option == "--embed":
|
||||
Options.embed = pop_value("main")
|
||||
elif option.startswith("-I"):
|
||||
options.include_path.append(get_param(option))
|
||||
elif option == "--include-dir":
|
||||
options.include_path.append(pop_value())
|
||||
elif option in ("-w", "--working"):
|
||||
options.working_path = pop_value()
|
||||
elif option in ("-o", "--output-file"):
|
||||
options.output_file = pop_value()
|
||||
elif option in ("-t", "--timestamps"):
|
||||
options.timestamps = 1
|
||||
elif option in ("-f", "--force"):
|
||||
options.timestamps = 0
|
||||
elif option in ("-v", "--verbose"):
|
||||
options.verbose += 1
|
||||
elif option in ("-p", "--embed-positions"):
|
||||
Options.embed_pos_in_docstring = 1
|
||||
elif option in ("-z", "--pre-import"):
|
||||
Options.pre_import = pop_value()
|
||||
elif option == "--cleanup":
|
||||
Options.generate_cleanup_code = int(pop_value())
|
||||
elif option in ("-D", "--no-docstrings"):
|
||||
Options.docstrings = False
|
||||
elif option in ("-a", "--annotate"):
|
||||
Options.annotate = True
|
||||
elif option == "--annotate-coverage":
|
||||
Options.annotate = True
|
||||
Options.annotate_coverage_xml = pop_value()
|
||||
elif option == "--convert-range":
|
||||
Options.convert_range = True
|
||||
elif option == "--line-directives":
|
||||
options.emit_linenums = True
|
||||
elif option == "--no-c-in-traceback":
|
||||
options.c_line_in_traceback = False
|
||||
elif option == "--gdb":
|
||||
options.gdb_debug = True
|
||||
options.output_dir = os.curdir
|
||||
elif option == "--gdb-outdir":
|
||||
options.gdb_debug = True
|
||||
options.output_dir = pop_value()
|
||||
elif option == "--lenient":
|
||||
Options.error_on_unknown_names = False
|
||||
Options.error_on_uninitialized = False
|
||||
elif option == '-2':
|
||||
options.language_level = 2
|
||||
elif option == '-3':
|
||||
options.language_level = 3
|
||||
elif option == '--3str':
|
||||
options.language_level = '3str'
|
||||
elif option == "--capi-reexport-cincludes":
|
||||
options.capi_reexport_cincludes = True
|
||||
elif option == "--fast-fail":
|
||||
Options.fast_fail = True
|
||||
elif option == "--cimport-from-pyx":
|
||||
Options.cimport_from_pyx = True
|
||||
elif option in ('-Werror', '--warning-errors'):
|
||||
Options.warning_errors = True
|
||||
elif option in ('-Wextra', '--warning-extra'):
|
||||
options.compiler_directives.update(Options.extra_warnings)
|
||||
elif option == "--old-style-globals":
|
||||
Options.old_style_globals = True
|
||||
elif option == "--directive" or option.startswith('-X'):
|
||||
if option.startswith('-X') and option[2:].strip():
|
||||
x_args = option[2:]
|
||||
else:
|
||||
x_args = pop_value()
|
||||
try:
|
||||
options.compiler_directives = Options.parse_directive_list(
|
||||
x_args, relaxed_bool=True,
|
||||
current_settings=options.compiler_directives)
|
||||
except ValueError as e:
|
||||
sys.stderr.write("Error in compiler directive: %s\n" % e.args[0])
|
||||
sys.exit(1)
|
||||
elif option == "--compile-time-env" or option.startswith('-E'):
|
||||
if option.startswith('-E') and option[2:].strip():
|
||||
x_args = option[2:]
|
||||
else:
|
||||
x_args = pop_value()
|
||||
try:
|
||||
options.compile_time_env = Options.parse_compile_time_env(
|
||||
x_args, current_settings=options.compile_time_env)
|
||||
except ValueError as e:
|
||||
sys.stderr.write("Error in compile-time-env: %s\n" % e.args[0])
|
||||
sys.exit(1)
|
||||
elif option == "--module-name":
|
||||
options.module_name = pop_value()
|
||||
elif option.startswith('--debug'):
|
||||
option = option[2:].replace('-', '_')
|
||||
from . import DebugFlags
|
||||
if option in dir(DebugFlags):
|
||||
setattr(DebugFlags, option, True)
|
||||
else:
|
||||
sys.stderr.write("Unknown debug flag: %s\n" % option)
|
||||
bad_usage()
|
||||
elif option in ('-h', '--help'):
|
||||
sys.stdout.write(usage)
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.stderr.write(usage)
|
||||
sys.stderr.write("Unknown compiler flag: %s\n" % option)
|
||||
sys.exit(1)
|
||||
else:
|
||||
sources.append(pop_arg())
|
||||
|
||||
if pending_arg:
|
||||
bad_usage()
|
||||
|
||||
if options.use_listing_file and len(sources) > 1:
|
||||
sys.stderr.write(
|
||||
"cython: Only one source file allowed when using -o\n")
|
||||
sys.exit(1)
|
||||
if len(sources) == 0 and not options.show_version:
|
||||
bad_usage()
|
||||
if Options.embed and len(sources) > 1:
|
||||
sys.stderr.write(
|
||||
"cython: Only one source file allowed when using --embed\n")
|
||||
sys.exit(1)
|
||||
if options.module_name:
|
||||
if options.timestamps:
|
||||
sys.stderr.write(
|
||||
"cython: Cannot use --module-name with --timestamps\n")
|
||||
sys.exit(1)
|
||||
if len(sources) > 1:
|
||||
sys.stderr.write(
|
||||
"cython: Only one source file allowed when using --module-name\n")
|
||||
sys.exit(1)
|
||||
return options, sources
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
cimport cython
|
||||
from ..StringIOTree cimport StringIOTree
|
||||
|
||||
|
||||
cdef class UtilityCodeBase(object):
|
||||
cpdef format_code(self, code_string, replace_empty_lines=*)
|
||||
|
||||
|
||||
cdef class UtilityCode(UtilityCodeBase):
|
||||
cdef public object name
|
||||
cdef public object proto
|
||||
cdef public object impl
|
||||
cdef public object init
|
||||
cdef public object cleanup
|
||||
cdef public object proto_block
|
||||
cdef public object requires
|
||||
cdef public dict _cache
|
||||
cdef public list specialize_list
|
||||
cdef public object file
|
||||
|
||||
cpdef none_or_sub(self, s, context)
|
||||
|
||||
|
||||
cdef class FunctionState:
|
||||
cdef public set names_taken
|
||||
cdef public object owner
|
||||
cdef public object scope
|
||||
|
||||
cdef public object error_label
|
||||
cdef public size_t label_counter
|
||||
cdef public set labels_used
|
||||
cdef public object return_label
|
||||
cdef public object continue_label
|
||||
cdef public object break_label
|
||||
cdef public list yield_labels
|
||||
|
||||
cdef public object return_from_error_cleanup_label # not used in __init__ ?
|
||||
|
||||
cdef public object exc_vars
|
||||
cdef public object current_except
|
||||
cdef public bint in_try_finally
|
||||
cdef public bint can_trace
|
||||
cdef public bint gil_owned
|
||||
|
||||
cdef public list temps_allocated
|
||||
cdef public dict temps_free
|
||||
cdef public dict temps_used_type
|
||||
cdef public set zombie_temps
|
||||
cdef public size_t temp_counter
|
||||
cdef public list collect_temps_stack
|
||||
|
||||
cdef public object closure_temps
|
||||
cdef public bint should_declare_error_indicator
|
||||
cdef public bint uses_error_indicator
|
||||
|
||||
@cython.locals(n=size_t)
|
||||
cpdef new_label(self, name=*)
|
||||
cpdef tuple get_loop_labels(self)
|
||||
cpdef set_loop_labels(self, labels)
|
||||
cpdef tuple get_all_labels(self)
|
||||
cpdef set_all_labels(self, labels)
|
||||
cpdef start_collecting_temps(self)
|
||||
cpdef stop_collecting_temps(self)
|
||||
|
||||
cpdef list temps_in_use(self)
|
||||
|
||||
cdef class IntConst:
|
||||
cdef public object cname
|
||||
cdef public object value
|
||||
cdef public bint is_long
|
||||
|
||||
cdef class PyObjectConst:
|
||||
cdef public object cname
|
||||
cdef public object type
|
||||
|
||||
cdef class StringConst:
|
||||
cdef public object cname
|
||||
cdef public object text
|
||||
cdef public object escaped_value
|
||||
cdef public dict py_strings
|
||||
cdef public list py_versions
|
||||
|
||||
@cython.locals(intern=bint, is_str=bint, is_unicode=bint)
|
||||
cpdef get_py_string_const(self, encoding, identifier=*, is_str=*, py3str_cstring=*)
|
||||
|
||||
## cdef class PyStringConst:
|
||||
## cdef public object cname
|
||||
## cdef public object encoding
|
||||
## cdef public bint is_str
|
||||
## cdef public bint is_unicode
|
||||
## cdef public bint intern
|
||||
|
||||
#class GlobalState(object):
|
||||
|
||||
#def funccontext_property(name):
|
||||
|
||||
cdef class CCodeWriter(object):
|
||||
cdef readonly StringIOTree buffer
|
||||
cdef readonly list pyclass_stack
|
||||
cdef readonly object globalstate
|
||||
cdef readonly object funcstate
|
||||
cdef object code_config
|
||||
cdef object last_pos
|
||||
cdef object last_marked_pos
|
||||
cdef Py_ssize_t level
|
||||
cdef public Py_ssize_t call_level # debug-only, see Nodes.py
|
||||
cdef bint bol
|
||||
|
||||
cpdef write(self, s)
|
||||
cpdef put(self, code)
|
||||
cpdef put_safe(self, code)
|
||||
cpdef putln(self, code=*, bint safe=*)
|
||||
@cython.final
|
||||
cdef increase_indent(self)
|
||||
@cython.final
|
||||
cdef decrease_indent(self)
|
||||
|
||||
|
||||
cdef class PyrexCodeWriter:
|
||||
cdef public object f
|
||||
cdef public Py_ssize_t level
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Visitor import VisitorTransform
|
||||
from .Nodes import StatListNode
|
||||
|
||||
|
||||
class ExtractPxdCode(VisitorTransform):
|
||||
"""
|
||||
Finds nodes in a pxd file that should generate code, and
|
||||
returns them in a StatListNode.
|
||||
|
||||
The result is a tuple (StatListNode, ModuleScope), i.e.
|
||||
everything that is needed from the pxd after it is processed.
|
||||
|
||||
A purer approach would be to separately compile the pxd code,
|
||||
but the result would have to be slightly more sophisticated
|
||||
than pure strings (functions + wanted interned strings +
|
||||
wanted utility code + wanted cached objects) so for now this
|
||||
approach is taken.
|
||||
"""
|
||||
|
||||
def __call__(self, root):
|
||||
self.funcs = []
|
||||
self.visitchildren(root)
|
||||
return (StatListNode(root.pos, stats=self.funcs), root.scope)
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
self.funcs.append(node)
|
||||
# Do not visit children, nested funcdefnodes will
|
||||
# also be moved by this action...
|
||||
return node
|
||||
|
||||
def visit_Node(self, node):
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
@@ -0,0 +1,164 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Symtab import ModuleScope
|
||||
from .PyrexTypes import *
|
||||
from .UtilityCode import CythonUtilityCode
|
||||
from .Errors import error
|
||||
from .Scanning import StringSourceDescriptor
|
||||
from . import MemoryView
|
||||
|
||||
|
||||
class CythonScope(ModuleScope):
|
||||
is_cython_builtin = 1
|
||||
_cythonscope_initialized = False
|
||||
|
||||
def __init__(self, context):
|
||||
ModuleScope.__init__(self, u'cython', None, None)
|
||||
self.pxd_file_loaded = True
|
||||
self.populate_cython_scope()
|
||||
# The Main.Context object
|
||||
self.context = context
|
||||
|
||||
for fused_type in (cy_integral_type, cy_floating_type, cy_numeric_type):
|
||||
entry = self.declare_typedef(fused_type.name,
|
||||
fused_type,
|
||||
None,
|
||||
cname='<error>')
|
||||
entry.in_cinclude = True
|
||||
|
||||
def is_cpp(self):
|
||||
# Allow C++ utility code in C++ contexts.
|
||||
return self.context.cpp
|
||||
|
||||
def lookup_type(self, name):
|
||||
# This function should go away when types are all first-level objects.
|
||||
type = parse_basic_type(name)
|
||||
if type:
|
||||
return type
|
||||
|
||||
return super(CythonScope, self).lookup_type(name)
|
||||
|
||||
def lookup(self, name):
|
||||
entry = super(CythonScope, self).lookup(name)
|
||||
|
||||
if entry is None and not self._cythonscope_initialized:
|
||||
self.load_cythonscope()
|
||||
entry = super(CythonScope, self).lookup(name)
|
||||
|
||||
return entry
|
||||
|
||||
def find_module(self, module_name, pos):
|
||||
error("cython.%s is not available" % module_name, pos)
|
||||
|
||||
def find_submodule(self, module_name):
|
||||
entry = self.entries.get(module_name, None)
|
||||
if not entry:
|
||||
self.load_cythonscope()
|
||||
entry = self.entries.get(module_name, None)
|
||||
|
||||
if entry and entry.as_module:
|
||||
return entry.as_module
|
||||
else:
|
||||
# TODO: fix find_submodule control flow so that we're not
|
||||
# expected to create a submodule here (to protect CythonScope's
|
||||
# possible immutability). Hack ourselves out of the situation
|
||||
# for now.
|
||||
raise error((StringSourceDescriptor(u"cython", u""), 0, 0),
|
||||
"cython.%s is not available" % module_name)
|
||||
|
||||
def lookup_qualified_name(self, qname):
|
||||
# ExprNode.as_cython_attribute generates qnames and we untangle it here...
|
||||
name_path = qname.split(u'.')
|
||||
scope = self
|
||||
while len(name_path) > 1:
|
||||
scope = scope.lookup_here(name_path[0])
|
||||
if scope:
|
||||
scope = scope.as_module
|
||||
del name_path[0]
|
||||
if scope is None:
|
||||
return None
|
||||
else:
|
||||
return scope.lookup_here(name_path[0])
|
||||
|
||||
def populate_cython_scope(self):
|
||||
# These are used to optimize isinstance in FinalOptimizePhase
|
||||
type_object = self.declare_typedef(
|
||||
'PyTypeObject',
|
||||
base_type = c_void_type,
|
||||
pos = None,
|
||||
cname = 'PyTypeObject')
|
||||
type_object.is_void = True
|
||||
type_object_type = type_object.type
|
||||
|
||||
self.declare_cfunction(
|
||||
'PyObject_TypeCheck',
|
||||
CFuncType(c_bint_type, [CFuncTypeArg("o", py_object_type, None),
|
||||
CFuncTypeArg("t", c_ptr_type(type_object_type), None)]),
|
||||
pos = None,
|
||||
defining = 1,
|
||||
cname = 'PyObject_TypeCheck')
|
||||
|
||||
def load_cythonscope(self):
|
||||
"""
|
||||
Creates some entries for testing purposes and entries for
|
||||
cython.array() and for cython.view.*.
|
||||
"""
|
||||
if self._cythonscope_initialized:
|
||||
return
|
||||
|
||||
self._cythonscope_initialized = True
|
||||
cython_testscope_utility_code.declare_in_scope(
|
||||
self, cython_scope=self)
|
||||
cython_test_extclass_utility_code.declare_in_scope(
|
||||
self, cython_scope=self)
|
||||
|
||||
#
|
||||
# The view sub-scope
|
||||
#
|
||||
self.viewscope = viewscope = ModuleScope(u'view', self, None)
|
||||
self.declare_module('view', viewscope, None).as_module = viewscope
|
||||
viewscope.is_cython_builtin = True
|
||||
viewscope.pxd_file_loaded = True
|
||||
|
||||
cythonview_testscope_utility_code.declare_in_scope(
|
||||
viewscope, cython_scope=self)
|
||||
|
||||
view_utility_scope = MemoryView.view_utility_code.declare_in_scope(
|
||||
self.viewscope, cython_scope=self,
|
||||
whitelist=MemoryView.view_utility_whitelist)
|
||||
|
||||
# self.entries["array"] = view_utility_scope.entries.pop("array")
|
||||
|
||||
|
||||
def create_cython_scope(context):
|
||||
# One could in fact probably make it a singleton,
|
||||
# but not sure yet whether any code mutates it (which would kill reusing
|
||||
# it across different contexts)
|
||||
return CythonScope(context)
|
||||
|
||||
# Load test utilities for the cython scope
|
||||
|
||||
def load_testscope_utility(cy_util_name, **kwargs):
|
||||
return CythonUtilityCode.load(cy_util_name, "TestCythonScope.pyx", **kwargs)
|
||||
|
||||
|
||||
undecorated_methods_protos = UtilityCode(proto=u"""
|
||||
/* These methods are undecorated and have therefore no prototype */
|
||||
static PyObject *__pyx_TestClass_cdef_method(
|
||||
struct __pyx_TestClass_obj *self, int value);
|
||||
static PyObject *__pyx_TestClass_cpdef_method(
|
||||
struct __pyx_TestClass_obj *self, int value, int skip_dispatch);
|
||||
static PyObject *__pyx_TestClass_def_method(
|
||||
PyObject *self, PyObject *value);
|
||||
""")
|
||||
|
||||
cython_testscope_utility_code = load_testscope_utility("TestScope")
|
||||
|
||||
test_cython_utility_dep = load_testscope_utility("TestDep")
|
||||
|
||||
cython_test_extclass_utility_code = \
|
||||
load_testscope_utility("TestClass", name="TestClass",
|
||||
requires=[undecorated_methods_protos,
|
||||
test_cython_utility_dep])
|
||||
|
||||
cythonview_testscope_utility_code = load_testscope_utility("View.TestScope")
|
||||
@@ -0,0 +1,21 @@
|
||||
# Can be enabled at the command line with --debug-xxx.
|
||||
|
||||
debug_disposal_code = 0
|
||||
debug_temp_alloc = 0
|
||||
debug_coercion = 0
|
||||
|
||||
# Write comments into the C code that show where temporary variables
|
||||
# are allocated and released.
|
||||
debug_temp_code_comments = 0
|
||||
|
||||
# Write a call trace of the code generation phase into the C code.
|
||||
debug_trace_code_generation = 0
|
||||
|
||||
# Do not replace exceptions with user-friendly error messages.
|
||||
debug_no_exception_intercept = 0
|
||||
|
||||
# Print a message each time a new stage in the pipeline is entered.
|
||||
debug_verbose_pipeline = 0
|
||||
|
||||
# Raise an exception when an error is encountered.
|
||||
debug_exception_on_error = 0
|
||||
@@ -0,0 +1,265 @@
|
||||
#
|
||||
# Errors
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
try:
|
||||
from __builtin__ import basestring as any_string_type
|
||||
except ImportError:
|
||||
any_string_type = (bytes, str)
|
||||
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ..Utils import open_new_file
|
||||
from . import DebugFlags
|
||||
from . import Options
|
||||
|
||||
|
||||
class PyrexError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PyrexWarning(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def context(position):
|
||||
source = position[0]
|
||||
assert not (isinstance(source, any_string_type)), (
|
||||
"Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
|
||||
try:
|
||||
F = source.get_lines()
|
||||
except UnicodeDecodeError:
|
||||
# file has an encoding problem
|
||||
s = u"[unprintable code]\n"
|
||||
else:
|
||||
s = u''.join(F[max(0, position[1]-6):position[1]])
|
||||
s = u'...\n%s%s^\n' % (s, u' '*(position[2]-1))
|
||||
s = u'%s\n%s%s\n' % (u'-'*60, s, u'-'*60)
|
||||
return s
|
||||
|
||||
def format_position(position):
|
||||
if position:
|
||||
return u"%s:%d:%d: " % (position[0].get_error_description(),
|
||||
position[1], position[2])
|
||||
return u''
|
||||
|
||||
def format_error(message, position):
|
||||
if position:
|
||||
pos_str = format_position(position)
|
||||
cont = context(position)
|
||||
message = u'\nError compiling Cython file:\n%s\n%s%s' % (cont, pos_str, message or u'')
|
||||
return message
|
||||
|
||||
class CompileError(PyrexError):
|
||||
|
||||
def __init__(self, position = None, message = u""):
|
||||
self.position = position
|
||||
self.message_only = message
|
||||
self.formatted_message = format_error(message, position)
|
||||
self.reported = False
|
||||
# Deprecated and withdrawn in 2.6:
|
||||
# self.message = message
|
||||
Exception.__init__(self, self.formatted_message)
|
||||
# Python Exception subclass pickling is broken,
|
||||
# see http://bugs.python.org/issue1692335
|
||||
self.args = (position, message)
|
||||
|
||||
def __str__(self):
|
||||
return self.formatted_message
|
||||
|
||||
class CompileWarning(PyrexWarning):
|
||||
|
||||
def __init__(self, position = None, message = ""):
|
||||
self.position = position
|
||||
# Deprecated and withdrawn in 2.6:
|
||||
# self.message = message
|
||||
Exception.__init__(self, format_position(position) + message)
|
||||
|
||||
class InternalError(Exception):
|
||||
# If this is ever raised, there is a bug in the compiler.
|
||||
|
||||
def __init__(self, message):
|
||||
self.message_only = message
|
||||
Exception.__init__(self, u"Internal compiler error: %s"
|
||||
% message)
|
||||
|
||||
class AbortError(Exception):
|
||||
# Throw this to stop the compilation immediately.
|
||||
|
||||
def __init__(self, message):
|
||||
self.message_only = message
|
||||
Exception.__init__(self, u"Abort error: %s" % message)
|
||||
|
||||
class CompilerCrash(CompileError):
|
||||
# raised when an unexpected exception occurs in a transform
|
||||
def __init__(self, pos, context, message, cause, stacktrace=None):
|
||||
if message:
|
||||
message = u'\n' + message
|
||||
else:
|
||||
message = u'\n'
|
||||
self.message_only = message
|
||||
if context:
|
||||
message = u"Compiler crash in %s%s" % (context, message)
|
||||
if stacktrace:
|
||||
import traceback
|
||||
message += (
|
||||
u'\n\nCompiler crash traceback from this point on:\n' +
|
||||
u''.join(traceback.format_tb(stacktrace)))
|
||||
if cause:
|
||||
if not stacktrace:
|
||||
message += u'\n'
|
||||
message += u'%s: %s' % (cause.__class__.__name__, cause)
|
||||
CompileError.__init__(self, pos, message)
|
||||
# Python Exception subclass pickling is broken,
|
||||
# see http://bugs.python.org/issue1692335
|
||||
self.args = (pos, context, message, cause, stacktrace)
|
||||
|
||||
class NoElementTreeInstalledException(PyrexError):
|
||||
"""raised when the user enabled options.gdb_debug but no ElementTree
|
||||
implementation was found
|
||||
"""
|
||||
|
||||
listing_file = None
|
||||
num_errors = 0
|
||||
echo_file = None
|
||||
|
||||
def open_listing_file(path, echo_to_stderr = 1):
|
||||
# Begin a new error listing. If path is None, no file
|
||||
# is opened, the error counter is just reset.
|
||||
global listing_file, num_errors, echo_file
|
||||
if path is not None:
|
||||
listing_file = open_new_file(path)
|
||||
else:
|
||||
listing_file = None
|
||||
if echo_to_stderr:
|
||||
echo_file = sys.stderr
|
||||
else:
|
||||
echo_file = None
|
||||
num_errors = 0
|
||||
|
||||
def close_listing_file():
|
||||
global listing_file
|
||||
if listing_file:
|
||||
listing_file.close()
|
||||
listing_file = None
|
||||
|
||||
def report_error(err, use_stack=True):
|
||||
if error_stack and use_stack:
|
||||
error_stack[-1].append(err)
|
||||
else:
|
||||
global num_errors
|
||||
# See Main.py for why dual reporting occurs. Quick fix for now.
|
||||
if err.reported: return
|
||||
err.reported = True
|
||||
try: line = u"%s\n" % err
|
||||
except UnicodeEncodeError:
|
||||
# Python <= 2.5 does this for non-ASCII Unicode exceptions
|
||||
line = format_error(getattr(err, 'message_only', "[unprintable exception message]"),
|
||||
getattr(err, 'position', None)) + u'\n'
|
||||
if listing_file:
|
||||
try: listing_file.write(line)
|
||||
except UnicodeEncodeError:
|
||||
listing_file.write(line.encode('ASCII', 'replace'))
|
||||
if echo_file:
|
||||
try: echo_file.write(line)
|
||||
except UnicodeEncodeError:
|
||||
echo_file.write(line.encode('ASCII', 'replace'))
|
||||
num_errors += 1
|
||||
if Options.fast_fail:
|
||||
raise AbortError("fatal errors")
|
||||
|
||||
|
||||
def error(position, message):
|
||||
#print("Errors.error:", repr(position), repr(message)) ###
|
||||
if position is None:
|
||||
raise InternalError(message)
|
||||
err = CompileError(position, message)
|
||||
if DebugFlags.debug_exception_on_error: raise Exception(err) # debug
|
||||
report_error(err)
|
||||
return err
|
||||
|
||||
|
||||
LEVEL = 1 # warn about all errors level 1 or higher
|
||||
|
||||
|
||||
def message(position, message, level=1):
|
||||
if level < LEVEL:
|
||||
return
|
||||
warn = CompileWarning(position, message)
|
||||
line = "note: %s\n" % warn
|
||||
if listing_file:
|
||||
listing_file.write(line)
|
||||
if echo_file:
|
||||
echo_file.write(line)
|
||||
return warn
|
||||
|
||||
|
||||
def warning(position, message, level=0):
|
||||
if level < LEVEL:
|
||||
return
|
||||
if Options.warning_errors and position:
|
||||
return error(position, message)
|
||||
warn = CompileWarning(position, message)
|
||||
line = "warning: %s\n" % warn
|
||||
if listing_file:
|
||||
listing_file.write(line)
|
||||
if echo_file:
|
||||
echo_file.write(line)
|
||||
return warn
|
||||
|
||||
|
||||
_warn_once_seen = {}
|
||||
def warn_once(position, message, level=0):
|
||||
if level < LEVEL or message in _warn_once_seen:
|
||||
return
|
||||
warn = CompileWarning(position, message)
|
||||
line = "warning: %s\n" % warn
|
||||
if listing_file:
|
||||
listing_file.write(line)
|
||||
if echo_file:
|
||||
echo_file.write(line)
|
||||
_warn_once_seen[message] = True
|
||||
return warn
|
||||
|
||||
|
||||
# These functions can be used to momentarily suppress errors.
|
||||
|
||||
error_stack = []
|
||||
|
||||
|
||||
def hold_errors():
|
||||
error_stack.append([])
|
||||
|
||||
|
||||
def release_errors(ignore=False):
|
||||
held_errors = error_stack.pop()
|
||||
if not ignore:
|
||||
for err in held_errors:
|
||||
report_error(err)
|
||||
|
||||
|
||||
def held_errors():
|
||||
return error_stack[-1]
|
||||
|
||||
|
||||
# same as context manager:
|
||||
|
||||
@contextmanager
|
||||
def local_errors(ignore=False):
|
||||
errors = []
|
||||
error_stack.append(errors)
|
||||
try:
|
||||
yield errors
|
||||
finally:
|
||||
release_errors(ignore=ignore)
|
||||
|
||||
|
||||
# this module needs a redesign to support parallel cythonisation, but
|
||||
# for now, the following works at least in sequential compiler runs
|
||||
|
||||
def reset():
|
||||
_warn_once_seen.clear()
|
||||
del error_stack[:]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,111 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
cimport cython
|
||||
|
||||
from .Visitor cimport CythonTransform, TreeVisitor
|
||||
|
||||
cdef class ControlBlock:
|
||||
cdef public set children
|
||||
cdef public set parents
|
||||
cdef public set positions
|
||||
cdef public list stats
|
||||
cdef public dict gen
|
||||
cdef public set bounded
|
||||
|
||||
# Big integer bitsets
|
||||
cdef public object i_input
|
||||
cdef public object i_output
|
||||
cdef public object i_gen
|
||||
cdef public object i_kill
|
||||
cdef public object i_state
|
||||
|
||||
cpdef bint empty(self)
|
||||
cpdef detach(self)
|
||||
cpdef add_child(self, block)
|
||||
|
||||
cdef class ExitBlock(ControlBlock):
|
||||
cpdef bint empty(self)
|
||||
|
||||
cdef class NameAssignment:
|
||||
cdef public bint is_arg
|
||||
cdef public bint is_deletion
|
||||
cdef public object lhs
|
||||
cdef public object rhs
|
||||
cdef public object entry
|
||||
cdef public object pos
|
||||
cdef public set refs
|
||||
cdef public object bit
|
||||
cdef public object inferred_type
|
||||
|
||||
cdef class AssignmentList:
|
||||
cdef public object bit
|
||||
cdef public object mask
|
||||
cdef public list stats
|
||||
|
||||
cdef class AssignmentCollector(TreeVisitor):
|
||||
cdef list assignments
|
||||
|
||||
@cython.final
|
||||
cdef class ControlFlow:
|
||||
cdef public set blocks
|
||||
cdef public set entries
|
||||
cdef public list loops
|
||||
cdef public list exceptions
|
||||
|
||||
cdef public ControlBlock entry_point
|
||||
cdef public ExitBlock exit_point
|
||||
cdef public ControlBlock block
|
||||
|
||||
cdef public dict assmts
|
||||
|
||||
cpdef newblock(self, ControlBlock parent=*)
|
||||
cpdef nextblock(self, ControlBlock parent=*)
|
||||
cpdef bint is_tracked(self, entry)
|
||||
cpdef bint is_statically_assigned(self, entry)
|
||||
cpdef mark_position(self, node)
|
||||
cpdef mark_assignment(self, lhs, rhs, entry)
|
||||
cpdef mark_argument(self, lhs, rhs, entry)
|
||||
cpdef mark_deletion(self, node, entry)
|
||||
cpdef mark_reference(self, node, entry)
|
||||
|
||||
@cython.locals(block=ControlBlock, parent=ControlBlock, unreachable=set)
|
||||
cpdef normalize(self)
|
||||
|
||||
@cython.locals(bit=object, assmts=AssignmentList,
|
||||
block=ControlBlock)
|
||||
cpdef initialize(self)
|
||||
|
||||
@cython.locals(assmts=AssignmentList, assmt=NameAssignment)
|
||||
cpdef set map_one(self, istate, entry)
|
||||
|
||||
@cython.locals(block=ControlBlock, parent=ControlBlock)
|
||||
cdef reaching_definitions(self)
|
||||
|
||||
cdef class Uninitialized:
|
||||
pass
|
||||
|
||||
cdef class Unknown:
|
||||
pass
|
||||
|
||||
|
||||
cdef class MessageCollection:
|
||||
cdef set messages
|
||||
|
||||
|
||||
@cython.locals(dirty=bint, block=ControlBlock, parent=ControlBlock,
|
||||
assmt=NameAssignment)
|
||||
cdef check_definitions(ControlFlow flow, dict compiler_directives)
|
||||
|
||||
@cython.final
|
||||
cdef class ControlFlowAnalysis(CythonTransform):
|
||||
cdef object gv_ctx
|
||||
cdef object constant_folder
|
||||
cdef set reductions
|
||||
cdef list env_stack
|
||||
cdef list stack
|
||||
cdef object env
|
||||
cdef ControlFlow flow
|
||||
cdef bint in_inplace_assignment
|
||||
|
||||
cpdef mark_assignment(self, lhs, rhs=*)
|
||||
cpdef mark_position(self, node)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,901 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import copy
|
||||
|
||||
from . import (ExprNodes, PyrexTypes, MemoryView,
|
||||
ParseTreeTransforms, StringEncoding, Errors)
|
||||
from .ExprNodes import CloneNode, ProxyNode, TupleNode
|
||||
from .Nodes import FuncDefNode, CFuncDefNode, StatListNode, DefNode
|
||||
from ..Utils import OrderedSet
|
||||
|
||||
|
||||
class FusedCFuncDefNode(StatListNode):
|
||||
"""
|
||||
This node replaces a function with fused arguments. It deep-copies the
|
||||
function for every permutation of fused types, and allocates a new local
|
||||
scope for it. It keeps track of the original function in self.node, and
|
||||
the entry of the original function in the symbol table is given the
|
||||
'fused_cfunction' attribute which points back to us.
|
||||
Then when a function lookup occurs (to e.g. call it), the call can be
|
||||
dispatched to the right function.
|
||||
|
||||
node FuncDefNode the original function
|
||||
nodes [FuncDefNode] list of copies of node with different specific types
|
||||
py_func DefNode the fused python function subscriptable from
|
||||
Python space
|
||||
__signatures__ A DictNode mapping signature specialization strings
|
||||
to PyCFunction nodes
|
||||
resulting_fused_function PyCFunction for the fused DefNode that delegates
|
||||
to specializations
|
||||
fused_func_assignment Assignment of the fused function to the function name
|
||||
defaults_tuple TupleNode of defaults (letting PyCFunctionNode build
|
||||
defaults would result in many different tuples)
|
||||
specialized_pycfuncs List of synthesized pycfunction nodes for the
|
||||
specializations
|
||||
code_object CodeObjectNode shared by all specializations and the
|
||||
fused function
|
||||
|
||||
fused_compound_types All fused (compound) types (e.g. floating[:])
|
||||
"""
|
||||
|
||||
__signatures__ = None
|
||||
resulting_fused_function = None
|
||||
fused_func_assignment = None
|
||||
defaults_tuple = None
|
||||
decorators = None
|
||||
|
||||
child_attrs = StatListNode.child_attrs + [
|
||||
'__signatures__', 'resulting_fused_function', 'fused_func_assignment']
|
||||
|
||||
def __init__(self, node, env):
|
||||
super(FusedCFuncDefNode, self).__init__(node.pos)
|
||||
|
||||
self.nodes = []
|
||||
self.node = node
|
||||
|
||||
is_def = isinstance(self.node, DefNode)
|
||||
if is_def:
|
||||
# self.node.decorators = []
|
||||
self.copy_def(env)
|
||||
else:
|
||||
self.copy_cdef(env)
|
||||
|
||||
# Perform some sanity checks. If anything fails, it's a bug
|
||||
for n in self.nodes:
|
||||
assert not n.entry.type.is_fused
|
||||
assert not n.local_scope.return_type.is_fused
|
||||
if node.return_type.is_fused:
|
||||
assert not n.return_type.is_fused
|
||||
|
||||
if not is_def and n.cfunc_declarator.optional_arg_count:
|
||||
assert n.type.op_arg_struct
|
||||
|
||||
node.entry.fused_cfunction = self
|
||||
# Copy the nodes as AnalyseDeclarationsTransform will prepend
|
||||
# self.py_func to self.stats, as we only want specialized
|
||||
# CFuncDefNodes in self.nodes
|
||||
self.stats = self.nodes[:]
|
||||
|
||||
def copy_def(self, env):
|
||||
"""
|
||||
Create a copy of the original def or lambda function for specialized
|
||||
versions.
|
||||
"""
|
||||
fused_compound_types = PyrexTypes.unique(
|
||||
[arg.type for arg in self.node.args if arg.type.is_fused])
|
||||
fused_types = self._get_fused_base_types(fused_compound_types)
|
||||
permutations = PyrexTypes.get_all_specialized_permutations(fused_types)
|
||||
|
||||
self.fused_compound_types = fused_compound_types
|
||||
|
||||
if self.node.entry in env.pyfunc_entries:
|
||||
env.pyfunc_entries.remove(self.node.entry)
|
||||
|
||||
for cname, fused_to_specific in permutations:
|
||||
copied_node = copy.deepcopy(self.node)
|
||||
# keep signature object identity for special casing in DefNode.analyse_declarations()
|
||||
copied_node.entry.signature = self.node.entry.signature
|
||||
|
||||
self._specialize_function_args(copied_node.args, fused_to_specific)
|
||||
copied_node.return_type = self.node.return_type.specialize(
|
||||
fused_to_specific)
|
||||
|
||||
copied_node.analyse_declarations(env)
|
||||
# copied_node.is_staticmethod = self.node.is_staticmethod
|
||||
# copied_node.is_classmethod = self.node.is_classmethod
|
||||
self.create_new_local_scope(copied_node, env, fused_to_specific)
|
||||
self.specialize_copied_def(copied_node, cname, self.node.entry,
|
||||
fused_to_specific, fused_compound_types)
|
||||
|
||||
PyrexTypes.specialize_entry(copied_node.entry, cname)
|
||||
copied_node.entry.used = True
|
||||
env.entries[copied_node.entry.name] = copied_node.entry
|
||||
|
||||
if not self.replace_fused_typechecks(copied_node):
|
||||
break
|
||||
|
||||
self.orig_py_func = self.node
|
||||
self.py_func = self.make_fused_cpdef(self.node, env, is_def=True)
|
||||
|
||||
def copy_cdef(self, env):
|
||||
"""
|
||||
Create a copy of the original c(p)def function for all specialized
|
||||
versions.
|
||||
"""
|
||||
permutations = self.node.type.get_all_specialized_permutations()
|
||||
# print 'Node %s has %d specializations:' % (self.node.entry.name,
|
||||
# len(permutations))
|
||||
# import pprint; pprint.pprint([d for cname, d in permutations])
|
||||
|
||||
# Prevent copying of the python function
|
||||
self.orig_py_func = orig_py_func = self.node.py_func
|
||||
self.node.py_func = None
|
||||
if orig_py_func:
|
||||
env.pyfunc_entries.remove(orig_py_func.entry)
|
||||
|
||||
fused_types = self.node.type.get_fused_types()
|
||||
self.fused_compound_types = fused_types
|
||||
|
||||
new_cfunc_entries = []
|
||||
for cname, fused_to_specific in permutations:
|
||||
copied_node = copy.deepcopy(self.node)
|
||||
|
||||
# Make the types in our CFuncType specific.
|
||||
type = copied_node.type.specialize(fused_to_specific)
|
||||
entry = copied_node.entry
|
||||
type.specialize_entry(entry, cname)
|
||||
|
||||
# Reuse existing Entries (e.g. from .pxd files).
|
||||
for i, orig_entry in enumerate(env.cfunc_entries):
|
||||
if entry.cname == orig_entry.cname and type.same_as_resolved_type(orig_entry.type):
|
||||
copied_node.entry = env.cfunc_entries[i]
|
||||
if not copied_node.entry.func_cname:
|
||||
copied_node.entry.func_cname = entry.func_cname
|
||||
entry = copied_node.entry
|
||||
type = entry.type
|
||||
break
|
||||
else:
|
||||
new_cfunc_entries.append(entry)
|
||||
|
||||
copied_node.type = type
|
||||
entry.type, type.entry = type, entry
|
||||
|
||||
entry.used = (entry.used or
|
||||
self.node.entry.defined_in_pxd or
|
||||
env.is_c_class_scope or
|
||||
entry.is_cmethod)
|
||||
|
||||
if self.node.cfunc_declarator.optional_arg_count:
|
||||
self.node.cfunc_declarator.declare_optional_arg_struct(
|
||||
type, env, fused_cname=cname)
|
||||
|
||||
copied_node.return_type = type.return_type
|
||||
self.create_new_local_scope(copied_node, env, fused_to_specific)
|
||||
|
||||
# Make the argument types in the CFuncDeclarator specific
|
||||
self._specialize_function_args(copied_node.cfunc_declarator.args,
|
||||
fused_to_specific)
|
||||
|
||||
# If a cpdef, declare all specialized cpdefs (this
|
||||
# also calls analyse_declarations)
|
||||
copied_node.declare_cpdef_wrapper(env)
|
||||
if copied_node.py_func:
|
||||
env.pyfunc_entries.remove(copied_node.py_func.entry)
|
||||
|
||||
self.specialize_copied_def(
|
||||
copied_node.py_func, cname, self.node.entry.as_variable,
|
||||
fused_to_specific, fused_types)
|
||||
|
||||
if not self.replace_fused_typechecks(copied_node):
|
||||
break
|
||||
|
||||
# replace old entry with new entries
|
||||
try:
|
||||
cindex = env.cfunc_entries.index(self.node.entry)
|
||||
except ValueError:
|
||||
env.cfunc_entries.extend(new_cfunc_entries)
|
||||
else:
|
||||
env.cfunc_entries[cindex:cindex+1] = new_cfunc_entries
|
||||
|
||||
if orig_py_func:
|
||||
self.py_func = self.make_fused_cpdef(orig_py_func, env,
|
||||
is_def=False)
|
||||
else:
|
||||
self.py_func = orig_py_func
|
||||
|
||||
def _get_fused_base_types(self, fused_compound_types):
|
||||
"""
|
||||
Get a list of unique basic fused types, from a list of
|
||||
(possibly) compound fused types.
|
||||
"""
|
||||
base_types = []
|
||||
seen = set()
|
||||
for fused_type in fused_compound_types:
|
||||
fused_type.get_fused_types(result=base_types, seen=seen)
|
||||
return base_types
|
||||
|
||||
def _specialize_function_args(self, args, fused_to_specific):
|
||||
for arg in args:
|
||||
if arg.type.is_fused:
|
||||
arg.type = arg.type.specialize(fused_to_specific)
|
||||
if arg.type.is_memoryviewslice:
|
||||
arg.type.validate_memslice_dtype(arg.pos)
|
||||
|
||||
def create_new_local_scope(self, node, env, f2s):
|
||||
"""
|
||||
Create a new local scope for the copied node and append it to
|
||||
self.nodes. A new local scope is needed because the arguments with the
|
||||
fused types are already in the local scope, and we need the specialized
|
||||
entries created after analyse_declarations on each specialized version
|
||||
of the (CFunc)DefNode.
|
||||
f2s is a dict mapping each fused type to its specialized version
|
||||
"""
|
||||
node.create_local_scope(env)
|
||||
node.local_scope.fused_to_specific = f2s
|
||||
|
||||
# This is copied from the original function, set it to false to
|
||||
# stop recursion
|
||||
node.has_fused_arguments = False
|
||||
self.nodes.append(node)
|
||||
|
||||
def specialize_copied_def(self, node, cname, py_entry, f2s, fused_compound_types):
|
||||
"""Specialize the copy of a DefNode given the copied node,
|
||||
the specialization cname and the original DefNode entry"""
|
||||
fused_types = self._get_fused_base_types(fused_compound_types)
|
||||
type_strings = [
|
||||
PyrexTypes.specialization_signature_string(fused_type, f2s)
|
||||
for fused_type in fused_types
|
||||
]
|
||||
|
||||
node.specialized_signature_string = '|'.join(type_strings)
|
||||
|
||||
node.entry.pymethdef_cname = PyrexTypes.get_fused_cname(
|
||||
cname, node.entry.pymethdef_cname)
|
||||
node.entry.doc = py_entry.doc
|
||||
node.entry.doc_cname = py_entry.doc_cname
|
||||
|
||||
def replace_fused_typechecks(self, copied_node):
|
||||
"""
|
||||
Branch-prune fused type checks like
|
||||
|
||||
if fused_t is int:
|
||||
...
|
||||
|
||||
Returns whether an error was issued and whether we should stop in
|
||||
in order to prevent a flood of errors.
|
||||
"""
|
||||
num_errors = Errors.num_errors
|
||||
transform = ParseTreeTransforms.ReplaceFusedTypeChecks(
|
||||
copied_node.local_scope)
|
||||
transform(copied_node)
|
||||
|
||||
if Errors.num_errors > num_errors:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _fused_instance_checks(self, normal_types, pyx_code, env):
|
||||
"""
|
||||
Generate Cython code for instance checks, matching an object to
|
||||
specialized types.
|
||||
"""
|
||||
for specialized_type in normal_types:
|
||||
# all_numeric = all_numeric and specialized_type.is_numeric
|
||||
pyx_code.context.update(
|
||||
py_type_name=specialized_type.py_type_name(),
|
||||
specialized_type_name=specialized_type.specialization_string,
|
||||
)
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
if isinstance(arg, {{py_type_name}}):
|
||||
dest_sig[{{dest_sig_idx}}] = '{{specialized_type_name}}'; break
|
||||
""")
|
||||
|
||||
def _dtype_name(self, dtype):
|
||||
if dtype.is_typedef:
|
||||
return '___pyx_%s' % dtype
|
||||
return str(dtype).replace(' ', '_')
|
||||
|
||||
def _dtype_type(self, dtype):
|
||||
if dtype.is_typedef:
|
||||
return self._dtype_name(dtype)
|
||||
return str(dtype)
|
||||
|
||||
def _sizeof_dtype(self, dtype):
|
||||
if dtype.is_pyobject:
|
||||
return 'sizeof(void *)'
|
||||
else:
|
||||
return "sizeof(%s)" % self._dtype_type(dtype)
|
||||
|
||||
def _buffer_check_numpy_dtype_setup_cases(self, pyx_code):
|
||||
"Setup some common cases to match dtypes against specializations"
|
||||
if pyx_code.indenter("if kind in b'iu':"):
|
||||
pyx_code.putln("pass")
|
||||
pyx_code.named_insertion_point("dtype_int")
|
||||
pyx_code.dedent()
|
||||
|
||||
if pyx_code.indenter("elif kind == b'f':"):
|
||||
pyx_code.putln("pass")
|
||||
pyx_code.named_insertion_point("dtype_float")
|
||||
pyx_code.dedent()
|
||||
|
||||
if pyx_code.indenter("elif kind == b'c':"):
|
||||
pyx_code.putln("pass")
|
||||
pyx_code.named_insertion_point("dtype_complex")
|
||||
pyx_code.dedent()
|
||||
|
||||
if pyx_code.indenter("elif kind == b'O':"):
|
||||
pyx_code.putln("pass")
|
||||
pyx_code.named_insertion_point("dtype_object")
|
||||
pyx_code.dedent()
|
||||
|
||||
match = "dest_sig[{{dest_sig_idx}}] = '{{specialized_type_name}}'"
|
||||
no_match = "dest_sig[{{dest_sig_idx}}] = None"
|
||||
def _buffer_check_numpy_dtype(self, pyx_code, specialized_buffer_types, pythran_types):
|
||||
"""
|
||||
Match a numpy dtype object to the individual specializations.
|
||||
"""
|
||||
self._buffer_check_numpy_dtype_setup_cases(pyx_code)
|
||||
|
||||
for specialized_type in pythran_types+specialized_buffer_types:
|
||||
final_type = specialized_type
|
||||
if specialized_type.is_pythran_expr:
|
||||
specialized_type = specialized_type.org_buffer
|
||||
dtype = specialized_type.dtype
|
||||
pyx_code.context.update(
|
||||
itemsize_match=self._sizeof_dtype(dtype) + " == itemsize",
|
||||
signed_match="not (%s_is_signed ^ dtype_signed)" % self._dtype_name(dtype),
|
||||
dtype=dtype,
|
||||
specialized_type_name=final_type.specialization_string)
|
||||
|
||||
dtypes = [
|
||||
(dtype.is_int, pyx_code.dtype_int),
|
||||
(dtype.is_float, pyx_code.dtype_float),
|
||||
(dtype.is_complex, pyx_code.dtype_complex)
|
||||
]
|
||||
|
||||
for dtype_category, codewriter in dtypes:
|
||||
if dtype_category:
|
||||
cond = '{{itemsize_match}} and (<Py_ssize_t>arg.ndim) == %d' % (
|
||||
specialized_type.ndim,)
|
||||
if dtype.is_int:
|
||||
cond += ' and {{signed_match}}'
|
||||
|
||||
if final_type.is_pythran_expr:
|
||||
cond += ' and arg_is_pythran_compatible'
|
||||
|
||||
if codewriter.indenter("if %s:" % cond):
|
||||
#codewriter.putln("print 'buffer match found based on numpy dtype'")
|
||||
codewriter.putln(self.match)
|
||||
codewriter.putln("break")
|
||||
codewriter.dedent()
|
||||
|
||||
def _buffer_parse_format_string_check(self, pyx_code, decl_code,
|
||||
specialized_type, env):
|
||||
"""
|
||||
For each specialized type, try to coerce the object to a memoryview
|
||||
slice of that type. This means obtaining a buffer and parsing the
|
||||
format string.
|
||||
TODO: separate buffer acquisition from format parsing
|
||||
"""
|
||||
dtype = specialized_type.dtype
|
||||
if specialized_type.is_buffer:
|
||||
axes = [('direct', 'strided')] * specialized_type.ndim
|
||||
else:
|
||||
axes = specialized_type.axes
|
||||
|
||||
memslice_type = PyrexTypes.MemoryViewSliceType(dtype, axes)
|
||||
memslice_type.create_from_py_utility_code(env)
|
||||
pyx_code.context.update(
|
||||
coerce_from_py_func=memslice_type.from_py_function,
|
||||
dtype=dtype)
|
||||
decl_code.putln(
|
||||
"{{memviewslice_cname}} {{coerce_from_py_func}}(object, int)")
|
||||
|
||||
pyx_code.context.update(
|
||||
specialized_type_name=specialized_type.specialization_string,
|
||||
sizeof_dtype=self._sizeof_dtype(dtype))
|
||||
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
# try {{dtype}}
|
||||
if itemsize == -1 or itemsize == {{sizeof_dtype}}:
|
||||
memslice = {{coerce_from_py_func}}(arg, 0)
|
||||
if memslice.memview:
|
||||
__PYX_XDEC_MEMVIEW(&memslice, 1)
|
||||
# print 'found a match for the buffer through format parsing'
|
||||
%s
|
||||
break
|
||||
else:
|
||||
__pyx_PyErr_Clear()
|
||||
""" % self.match)
|
||||
|
||||
def _buffer_checks(self, buffer_types, pythran_types, pyx_code, decl_code, env):
|
||||
"""
|
||||
Generate Cython code to match objects to buffer specializations.
|
||||
First try to get a numpy dtype object and match it against the individual
|
||||
specializations. If that fails, try naively to coerce the object
|
||||
to each specialization, which obtains the buffer each time and tries
|
||||
to match the format string.
|
||||
"""
|
||||
# The first thing to find a match in this loop breaks out of the loop
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
""" + (u"arg_is_pythran_compatible = False" if pythran_types else u"") + u"""
|
||||
if ndarray is not None:
|
||||
if isinstance(arg, ndarray):
|
||||
dtype = arg.dtype
|
||||
""" + (u"arg_is_pythran_compatible = True" if pythran_types else u"") + u"""
|
||||
elif __pyx_memoryview_check(arg):
|
||||
arg_base = arg.base
|
||||
if isinstance(arg_base, ndarray):
|
||||
dtype = arg_base.dtype
|
||||
else:
|
||||
dtype = None
|
||||
else:
|
||||
dtype = None
|
||||
|
||||
itemsize = -1
|
||||
if dtype is not None:
|
||||
itemsize = dtype.itemsize
|
||||
kind = ord(dtype.kind)
|
||||
dtype_signed = kind == 'i'
|
||||
""")
|
||||
pyx_code.indent(2)
|
||||
if pythran_types:
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
# Pythran only supports the endianness of the current compiler
|
||||
byteorder = dtype.byteorder
|
||||
if byteorder == "<" and not __Pyx_Is_Little_Endian():
|
||||
arg_is_pythran_compatible = False
|
||||
elif byteorder == ">" and __Pyx_Is_Little_Endian():
|
||||
arg_is_pythran_compatible = False
|
||||
if arg_is_pythran_compatible:
|
||||
cur_stride = itemsize
|
||||
shape = arg.shape
|
||||
strides = arg.strides
|
||||
for i in range(arg.ndim-1, -1, -1):
|
||||
if (<Py_ssize_t>strides[i]) != cur_stride:
|
||||
arg_is_pythran_compatible = False
|
||||
break
|
||||
cur_stride *= <Py_ssize_t> shape[i]
|
||||
else:
|
||||
arg_is_pythran_compatible = not (arg.flags.f_contiguous and (<Py_ssize_t>arg.ndim) > 1)
|
||||
""")
|
||||
pyx_code.named_insertion_point("numpy_dtype_checks")
|
||||
self._buffer_check_numpy_dtype(pyx_code, buffer_types, pythran_types)
|
||||
pyx_code.dedent(2)
|
||||
|
||||
for specialized_type in buffer_types:
|
||||
self._buffer_parse_format_string_check(
|
||||
pyx_code, decl_code, specialized_type, env)
|
||||
|
||||
def _buffer_declarations(self, pyx_code, decl_code, all_buffer_types, pythran_types):
|
||||
"""
|
||||
If we have any buffer specializations, write out some variable
|
||||
declarations and imports.
|
||||
"""
|
||||
decl_code.put_chunk(
|
||||
u"""
|
||||
ctypedef struct {{memviewslice_cname}}:
|
||||
void *memview
|
||||
|
||||
void __PYX_XDEC_MEMVIEW({{memviewslice_cname}} *, int have_gil)
|
||||
bint __pyx_memoryview_check(object)
|
||||
""")
|
||||
|
||||
pyx_code.local_variable_declarations.put_chunk(
|
||||
u"""
|
||||
cdef {{memviewslice_cname}} memslice
|
||||
cdef Py_ssize_t itemsize
|
||||
cdef bint dtype_signed
|
||||
cdef char kind
|
||||
|
||||
itemsize = -1
|
||||
""")
|
||||
|
||||
if pythran_types:
|
||||
pyx_code.local_variable_declarations.put_chunk(u"""
|
||||
cdef bint arg_is_pythran_compatible
|
||||
cdef Py_ssize_t cur_stride
|
||||
""")
|
||||
|
||||
pyx_code.imports.put_chunk(
|
||||
u"""
|
||||
cdef type ndarray
|
||||
ndarray = __Pyx_ImportNumPyArrayTypeIfAvailable()
|
||||
""")
|
||||
|
||||
seen_typedefs = set()
|
||||
seen_int_dtypes = set()
|
||||
for buffer_type in all_buffer_types:
|
||||
dtype = buffer_type.dtype
|
||||
dtype_name = self._dtype_name(dtype)
|
||||
if dtype.is_typedef:
|
||||
if dtype_name not in seen_typedefs:
|
||||
seen_typedefs.add(dtype_name)
|
||||
decl_code.putln(
|
||||
'ctypedef %s %s "%s"' % (dtype.resolve(), dtype_name,
|
||||
dtype.empty_declaration_code()))
|
||||
|
||||
if buffer_type.dtype.is_int:
|
||||
if str(dtype) not in seen_int_dtypes:
|
||||
seen_int_dtypes.add(str(dtype))
|
||||
pyx_code.context.update(dtype_name=dtype_name,
|
||||
dtype_type=self._dtype_type(dtype))
|
||||
pyx_code.local_variable_declarations.put_chunk(
|
||||
u"""
|
||||
cdef bint {{dtype_name}}_is_signed
|
||||
{{dtype_name}}_is_signed = not (<{{dtype_type}}> -1 > 0)
|
||||
""")
|
||||
|
||||
def _split_fused_types(self, arg):
|
||||
"""
|
||||
Specialize fused types and split into normal types and buffer types.
|
||||
"""
|
||||
specialized_types = PyrexTypes.get_specialized_types(arg.type)
|
||||
|
||||
# Prefer long over int, etc by sorting (see type classes in PyrexTypes.py)
|
||||
specialized_types.sort()
|
||||
|
||||
seen_py_type_names = set()
|
||||
normal_types, buffer_types, pythran_types = [], [], []
|
||||
has_object_fallback = False
|
||||
for specialized_type in specialized_types:
|
||||
py_type_name = specialized_type.py_type_name()
|
||||
if py_type_name:
|
||||
if py_type_name in seen_py_type_names:
|
||||
continue
|
||||
seen_py_type_names.add(py_type_name)
|
||||
if py_type_name == 'object':
|
||||
has_object_fallback = True
|
||||
else:
|
||||
normal_types.append(specialized_type)
|
||||
elif specialized_type.is_pythran_expr:
|
||||
pythran_types.append(specialized_type)
|
||||
elif specialized_type.is_buffer or specialized_type.is_memoryviewslice:
|
||||
buffer_types.append(specialized_type)
|
||||
|
||||
return normal_types, buffer_types, pythran_types, has_object_fallback
|
||||
|
||||
def _unpack_argument(self, pyx_code):
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
# PROCESSING ARGUMENT {{arg_tuple_idx}}
|
||||
if {{arg_tuple_idx}} < len(<tuple>args):
|
||||
arg = (<tuple>args)[{{arg_tuple_idx}}]
|
||||
elif kwargs is not None and '{{arg.name}}' in <dict>kwargs:
|
||||
arg = (<dict>kwargs)['{{arg.name}}']
|
||||
else:
|
||||
{{if arg.default}}
|
||||
arg = (<tuple>defaults)[{{default_idx}}]
|
||||
{{else}}
|
||||
{{if arg_tuple_idx < min_positional_args}}
|
||||
raise TypeError("Expected at least %d argument%s, got %d" % (
|
||||
{{min_positional_args}}, {{'"s"' if min_positional_args != 1 else '""'}}, len(<tuple>args)))
|
||||
{{else}}
|
||||
raise TypeError("Missing keyword-only argument: '%s'" % "{{arg.default}}")
|
||||
{{endif}}
|
||||
{{endif}}
|
||||
""")
|
||||
|
||||
def make_fused_cpdef(self, orig_py_func, env, is_def):
|
||||
"""
|
||||
This creates the function that is indexable from Python and does
|
||||
runtime dispatch based on the argument types. The function gets the
|
||||
arg tuple and kwargs dict (or None) and the defaults tuple
|
||||
as arguments from the Binding Fused Function's tp_call.
|
||||
"""
|
||||
from . import TreeFragment, Code, UtilityCode
|
||||
|
||||
fused_types = self._get_fused_base_types([
|
||||
arg.type for arg in self.node.args if arg.type.is_fused])
|
||||
|
||||
context = {
|
||||
'memviewslice_cname': MemoryView.memviewslice_cname,
|
||||
'func_args': self.node.args,
|
||||
'n_fused': len(fused_types),
|
||||
'min_positional_args':
|
||||
self.node.num_required_args - self.node.num_required_kw_args
|
||||
if is_def else
|
||||
sum(1 for arg in self.node.args if arg.default is None),
|
||||
'name': orig_py_func.entry.name,
|
||||
}
|
||||
|
||||
pyx_code = Code.PyxCodeWriter(context=context)
|
||||
decl_code = Code.PyxCodeWriter(context=context)
|
||||
decl_code.put_chunk(
|
||||
u"""
|
||||
cdef extern from *:
|
||||
void __pyx_PyErr_Clear "PyErr_Clear" ()
|
||||
type __Pyx_ImportNumPyArrayTypeIfAvailable()
|
||||
int __Pyx_Is_Little_Endian()
|
||||
""")
|
||||
decl_code.indent()
|
||||
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
def __pyx_fused_cpdef(signatures, args, kwargs, defaults):
|
||||
# FIXME: use a typed signature - currently fails badly because
|
||||
# default arguments inherit the types we specify here!
|
||||
|
||||
dest_sig = [None] * {{n_fused}}
|
||||
|
||||
if kwargs is not None and not kwargs:
|
||||
kwargs = None
|
||||
|
||||
cdef Py_ssize_t i
|
||||
|
||||
# instance check body
|
||||
""")
|
||||
|
||||
pyx_code.indent() # indent following code to function body
|
||||
pyx_code.named_insertion_point("imports")
|
||||
pyx_code.named_insertion_point("func_defs")
|
||||
pyx_code.named_insertion_point("local_variable_declarations")
|
||||
|
||||
fused_index = 0
|
||||
default_idx = 0
|
||||
all_buffer_types = OrderedSet()
|
||||
seen_fused_types = set()
|
||||
for i, arg in enumerate(self.node.args):
|
||||
if arg.type.is_fused:
|
||||
arg_fused_types = arg.type.get_fused_types()
|
||||
if len(arg_fused_types) > 1:
|
||||
raise NotImplementedError("Determination of more than one fused base "
|
||||
"type per argument is not implemented.")
|
||||
fused_type = arg_fused_types[0]
|
||||
|
||||
if arg.type.is_fused and fused_type not in seen_fused_types:
|
||||
seen_fused_types.add(fused_type)
|
||||
|
||||
context.update(
|
||||
arg_tuple_idx=i,
|
||||
arg=arg,
|
||||
dest_sig_idx=fused_index,
|
||||
default_idx=default_idx,
|
||||
)
|
||||
|
||||
normal_types, buffer_types, pythran_types, has_object_fallback = self._split_fused_types(arg)
|
||||
self._unpack_argument(pyx_code)
|
||||
|
||||
# 'unrolled' loop, first match breaks out of it
|
||||
if pyx_code.indenter("while 1:"):
|
||||
if normal_types:
|
||||
self._fused_instance_checks(normal_types, pyx_code, env)
|
||||
if buffer_types or pythran_types:
|
||||
env.use_utility_code(Code.UtilityCode.load_cached("IsLittleEndian", "ModuleSetupCode.c"))
|
||||
self._buffer_checks(buffer_types, pythran_types, pyx_code, decl_code, env)
|
||||
if has_object_fallback:
|
||||
pyx_code.context.update(specialized_type_name='object')
|
||||
pyx_code.putln(self.match)
|
||||
else:
|
||||
pyx_code.putln(self.no_match)
|
||||
pyx_code.putln("break")
|
||||
pyx_code.dedent()
|
||||
|
||||
fused_index += 1
|
||||
all_buffer_types.update(buffer_types)
|
||||
all_buffer_types.update(ty.org_buffer for ty in pythran_types)
|
||||
|
||||
if arg.default:
|
||||
default_idx += 1
|
||||
|
||||
if all_buffer_types:
|
||||
self._buffer_declarations(pyx_code, decl_code, all_buffer_types, pythran_types)
|
||||
env.use_utility_code(Code.UtilityCode.load_cached("Import", "ImportExport.c"))
|
||||
env.use_utility_code(Code.UtilityCode.load_cached("ImportNumPyArray", "ImportExport.c"))
|
||||
|
||||
pyx_code.put_chunk(
|
||||
u"""
|
||||
candidates = []
|
||||
for sig in <dict>signatures:
|
||||
match_found = False
|
||||
src_sig = sig.strip('()').split('|')
|
||||
for i in range(len(dest_sig)):
|
||||
dst_type = dest_sig[i]
|
||||
if dst_type is not None:
|
||||
if src_sig[i] == dst_type:
|
||||
match_found = True
|
||||
else:
|
||||
match_found = False
|
||||
break
|
||||
|
||||
if match_found:
|
||||
candidates.append(sig)
|
||||
|
||||
if not candidates:
|
||||
raise TypeError("No matching signature found")
|
||||
elif len(candidates) > 1:
|
||||
raise TypeError("Function call with ambiguous argument types")
|
||||
else:
|
||||
return (<dict>signatures)[candidates[0]]
|
||||
""")
|
||||
|
||||
fragment_code = pyx_code.getvalue()
|
||||
# print decl_code.getvalue()
|
||||
# print fragment_code
|
||||
from .Optimize import ConstantFolding
|
||||
fragment = TreeFragment.TreeFragment(
|
||||
fragment_code, level='module', pipeline=[ConstantFolding()])
|
||||
ast = TreeFragment.SetPosTransform(self.node.pos)(fragment.root)
|
||||
UtilityCode.declare_declarations_in_scope(
|
||||
decl_code.getvalue(), env.global_scope())
|
||||
ast.scope = env
|
||||
# FIXME: for static methods of cdef classes, we build the wrong signature here: first arg becomes 'self'
|
||||
ast.analyse_declarations(env)
|
||||
py_func = ast.stats[-1] # the DefNode
|
||||
self.fragment_scope = ast.scope
|
||||
|
||||
if isinstance(self.node, DefNode):
|
||||
py_func.specialized_cpdefs = self.nodes[:]
|
||||
else:
|
||||
py_func.specialized_cpdefs = [n.py_func for n in self.nodes]
|
||||
|
||||
return py_func
|
||||
|
||||
def update_fused_defnode_entry(self, env):
|
||||
copy_attributes = (
|
||||
'name', 'pos', 'cname', 'func_cname', 'pyfunc_cname',
|
||||
'pymethdef_cname', 'doc', 'doc_cname', 'is_member',
|
||||
'scope'
|
||||
)
|
||||
|
||||
entry = self.py_func.entry
|
||||
|
||||
for attr in copy_attributes:
|
||||
setattr(entry, attr,
|
||||
getattr(self.orig_py_func.entry, attr))
|
||||
|
||||
self.py_func.name = self.orig_py_func.name
|
||||
self.py_func.doc = self.orig_py_func.doc
|
||||
|
||||
env.entries.pop('__pyx_fused_cpdef', None)
|
||||
if isinstance(self.node, DefNode):
|
||||
env.entries[entry.name] = entry
|
||||
else:
|
||||
env.entries[entry.name].as_variable = entry
|
||||
|
||||
env.pyfunc_entries.append(entry)
|
||||
|
||||
self.py_func.entry.fused_cfunction = self
|
||||
for node in self.nodes:
|
||||
if isinstance(self.node, DefNode):
|
||||
node.fused_py_func = self.py_func
|
||||
else:
|
||||
node.py_func.fused_py_func = self.py_func
|
||||
node.entry.as_variable = entry
|
||||
|
||||
self.synthesize_defnodes()
|
||||
self.stats.append(self.__signatures__)
|
||||
|
||||
def analyse_expressions(self, env):
|
||||
"""
|
||||
Analyse the expressions. Take care to only evaluate default arguments
|
||||
once and clone the result for all specializations
|
||||
"""
|
||||
for fused_compound_type in self.fused_compound_types:
|
||||
for fused_type in fused_compound_type.get_fused_types():
|
||||
for specialization_type in fused_type.types:
|
||||
if specialization_type.is_complex:
|
||||
specialization_type.create_declaration_utility_code(env)
|
||||
|
||||
if self.py_func:
|
||||
self.__signatures__ = self.__signatures__.analyse_expressions(env)
|
||||
self.py_func = self.py_func.analyse_expressions(env)
|
||||
self.resulting_fused_function = self.resulting_fused_function.analyse_expressions(env)
|
||||
self.fused_func_assignment = self.fused_func_assignment.analyse_expressions(env)
|
||||
|
||||
self.defaults = defaults = []
|
||||
|
||||
for arg in self.node.args:
|
||||
if arg.default:
|
||||
arg.default = arg.default.analyse_expressions(env)
|
||||
defaults.append(ProxyNode(arg.default))
|
||||
else:
|
||||
defaults.append(None)
|
||||
|
||||
for i, stat in enumerate(self.stats):
|
||||
stat = self.stats[i] = stat.analyse_expressions(env)
|
||||
if isinstance(stat, FuncDefNode):
|
||||
for arg, default in zip(stat.args, defaults):
|
||||
if default is not None:
|
||||
arg.default = CloneNode(default).coerce_to(arg.type, env)
|
||||
|
||||
if self.py_func:
|
||||
args = [CloneNode(default) for default in defaults if default]
|
||||
self.defaults_tuple = TupleNode(self.pos, args=args)
|
||||
self.defaults_tuple = self.defaults_tuple.analyse_types(env, skip_children=True).coerce_to_pyobject(env)
|
||||
self.defaults_tuple = ProxyNode(self.defaults_tuple)
|
||||
self.code_object = ProxyNode(self.specialized_pycfuncs[0].code_object)
|
||||
|
||||
fused_func = self.resulting_fused_function.arg
|
||||
fused_func.defaults_tuple = CloneNode(self.defaults_tuple)
|
||||
fused_func.code_object = CloneNode(self.code_object)
|
||||
|
||||
for i, pycfunc in enumerate(self.specialized_pycfuncs):
|
||||
pycfunc.code_object = CloneNode(self.code_object)
|
||||
pycfunc = self.specialized_pycfuncs[i] = pycfunc.analyse_types(env)
|
||||
pycfunc.defaults_tuple = CloneNode(self.defaults_tuple)
|
||||
return self
|
||||
|
||||
def synthesize_defnodes(self):
|
||||
"""
|
||||
Create the __signatures__ dict of PyCFunctionNode specializations.
|
||||
"""
|
||||
if isinstance(self.nodes[0], CFuncDefNode):
|
||||
nodes = [node.py_func for node in self.nodes]
|
||||
else:
|
||||
nodes = self.nodes
|
||||
|
||||
signatures = [StringEncoding.EncodedString(node.specialized_signature_string)
|
||||
for node in nodes]
|
||||
keys = [ExprNodes.StringNode(node.pos, value=sig)
|
||||
for node, sig in zip(nodes, signatures)]
|
||||
values = [ExprNodes.PyCFunctionNode.from_defnode(node, binding=True)
|
||||
for node in nodes]
|
||||
|
||||
self.__signatures__ = ExprNodes.DictNode.from_pairs(self.pos, zip(keys, values))
|
||||
|
||||
self.specialized_pycfuncs = values
|
||||
for pycfuncnode in values:
|
||||
pycfuncnode.is_specialization = True
|
||||
|
||||
def generate_function_definitions(self, env, code):
|
||||
if self.py_func:
|
||||
self.py_func.pymethdef_required = True
|
||||
self.fused_func_assignment.generate_function_definitions(env, code)
|
||||
|
||||
for stat in self.stats:
|
||||
if isinstance(stat, FuncDefNode) and stat.entry.used:
|
||||
code.mark_pos(stat.pos)
|
||||
stat.generate_function_definitions(env, code)
|
||||
|
||||
def generate_execution_code(self, code):
|
||||
# Note: all def function specialization are wrapped in PyCFunction
|
||||
# nodes in the self.__signatures__ dictnode.
|
||||
for default in self.defaults:
|
||||
if default is not None:
|
||||
default.generate_evaluation_code(code)
|
||||
|
||||
if self.py_func:
|
||||
self.defaults_tuple.generate_evaluation_code(code)
|
||||
self.code_object.generate_evaluation_code(code)
|
||||
|
||||
for stat in self.stats:
|
||||
code.mark_pos(stat.pos)
|
||||
if isinstance(stat, ExprNodes.ExprNode):
|
||||
stat.generate_evaluation_code(code)
|
||||
else:
|
||||
stat.generate_execution_code(code)
|
||||
|
||||
if self.__signatures__:
|
||||
self.resulting_fused_function.generate_evaluation_code(code)
|
||||
|
||||
code.putln(
|
||||
"((__pyx_FusedFunctionObject *) %s)->__signatures__ = %s;" %
|
||||
(self.resulting_fused_function.result(),
|
||||
self.__signatures__.result()))
|
||||
code.put_giveref(self.__signatures__.result())
|
||||
self.__signatures__.generate_post_assignment_code(code)
|
||||
self.__signatures__.free_temps(code)
|
||||
|
||||
self.fused_func_assignment.generate_execution_code(code)
|
||||
|
||||
# Dispose of results
|
||||
self.resulting_fused_function.generate_disposal_code(code)
|
||||
self.resulting_fused_function.free_temps(code)
|
||||
self.defaults_tuple.generate_disposal_code(code)
|
||||
self.defaults_tuple.free_temps(code)
|
||||
self.code_object.generate_disposal_code(code)
|
||||
self.code_object.free_temps(code)
|
||||
|
||||
for default in self.defaults:
|
||||
if default is not None:
|
||||
default.generate_disposal_code(code)
|
||||
default.free_temps(code)
|
||||
|
||||
def annotate(self, code):
|
||||
for stat in self.stats:
|
||||
stat.annotate(code)
|
||||
@@ -0,0 +1,15 @@
|
||||
def _get_feature(name):
|
||||
import __future__
|
||||
# fall back to a unique fake object for earlier Python versions or Python 3
|
||||
return getattr(__future__, name, object())
|
||||
|
||||
unicode_literals = _get_feature("unicode_literals")
|
||||
with_statement = _get_feature("with_statement") # dummy
|
||||
division = _get_feature("division")
|
||||
print_function = _get_feature("print_function")
|
||||
absolute_import = _get_feature("absolute_import")
|
||||
nested_scopes = _get_feature("nested_scopes") # dummy
|
||||
generators = _get_feature("generators") # dummy
|
||||
generator_stop = _get_feature("generator_stop")
|
||||
|
||||
del _get_feature
|
||||
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
This module deals with interpreting the parse tree as Python
|
||||
would have done, in the compiler.
|
||||
|
||||
For now this only covers parse tree to value conversion of
|
||||
compile-time values.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Nodes import *
|
||||
from .ExprNodes import *
|
||||
from .Errors import CompileError
|
||||
|
||||
|
||||
class EmptyScope(object):
|
||||
def lookup(self, name):
|
||||
return None
|
||||
|
||||
empty_scope = EmptyScope()
|
||||
|
||||
def interpret_compiletime_options(optlist, optdict, type_env=None, type_args=()):
|
||||
"""
|
||||
Tries to interpret a list of compile time option nodes.
|
||||
The result will be a tuple (optlist, optdict) but where
|
||||
all expression nodes have been interpreted. The result is
|
||||
in the form of tuples (value, pos).
|
||||
|
||||
optlist is a list of nodes, while optdict is a DictNode (the
|
||||
result optdict is a dict)
|
||||
|
||||
If type_env is set, all type nodes will be analysed and the resulting
|
||||
type set. Otherwise only interpretateable ExprNodes
|
||||
are allowed, other nodes raises errors.
|
||||
|
||||
A CompileError will be raised if there are problems.
|
||||
"""
|
||||
|
||||
def interpret(node, ix):
|
||||
if ix in type_args:
|
||||
if type_env:
|
||||
type = node.analyse_as_type(type_env)
|
||||
if not type:
|
||||
raise CompileError(node.pos, "Invalid type.")
|
||||
return (type, node.pos)
|
||||
else:
|
||||
raise CompileError(node.pos, "Type not allowed here.")
|
||||
else:
|
||||
if (sys.version_info[0] >=3 and
|
||||
isinstance(node, StringNode) and
|
||||
node.unicode_value is not None):
|
||||
return (node.unicode_value, node.pos)
|
||||
return (node.compile_time_value(empty_scope), node.pos)
|
||||
|
||||
if optlist:
|
||||
optlist = [interpret(x, ix) for ix, x in enumerate(optlist)]
|
||||
if optdict:
|
||||
assert isinstance(optdict, DictNode)
|
||||
new_optdict = {}
|
||||
for item in optdict.key_value_pairs:
|
||||
new_key, dummy = interpret(item.key, None)
|
||||
new_optdict[new_key] = interpret(item.value, item.key.value)
|
||||
optdict = new_optdict
|
||||
return (optlist, new_optdict)
|
||||
@@ -0,0 +1,138 @@
|
||||
# cython: language_level=3, py2_import=True
|
||||
#
|
||||
# Cython Scanner - Lexical Definitions
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
raw_prefixes = "rR"
|
||||
bytes_prefixes = "bB"
|
||||
string_prefixes = "fFuU" + bytes_prefixes
|
||||
char_prefixes = "cC"
|
||||
any_string_prefix = raw_prefixes + string_prefixes + char_prefixes
|
||||
IDENT = 'IDENT'
|
||||
|
||||
|
||||
def make_lexicon():
|
||||
from ..Plex import \
|
||||
Str, Any, AnyBut, AnyChar, Rep, Rep1, Opt, Bol, Eol, Eof, \
|
||||
TEXT, IGNORE, State, Lexicon
|
||||
from .Scanning import Method
|
||||
|
||||
letter = Any("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")
|
||||
digit = Any("0123456789")
|
||||
bindigit = Any("01")
|
||||
octdigit = Any("01234567")
|
||||
hexdigit = Any("0123456789ABCDEFabcdef")
|
||||
indentation = Bol + Rep(Any(" \t"))
|
||||
|
||||
def underscore_digits(d):
|
||||
return Rep1(d) + Rep(Str("_") + Rep1(d))
|
||||
|
||||
decimal = underscore_digits(digit)
|
||||
dot = Str(".")
|
||||
exponent = Any("Ee") + Opt(Any("+-")) + decimal
|
||||
decimal_fract = (decimal + dot + Opt(decimal)) | (dot + decimal)
|
||||
|
||||
name = letter + Rep(letter | digit)
|
||||
intconst = decimal | (Str("0") + ((Any("Xx") + underscore_digits(hexdigit)) |
|
||||
(Any("Oo") + underscore_digits(octdigit)) |
|
||||
(Any("Bb") + underscore_digits(bindigit)) ))
|
||||
intsuffix = (Opt(Any("Uu")) + Opt(Any("Ll")) + Opt(Any("Ll"))) | (Opt(Any("Ll")) + Opt(Any("Ll")) + Opt(Any("Uu")))
|
||||
intliteral = intconst + intsuffix
|
||||
fltconst = (decimal_fract + Opt(exponent)) | (decimal + exponent)
|
||||
imagconst = (intconst | fltconst) + Any("jJ")
|
||||
|
||||
# invalid combinations of prefixes are caught in p_string_literal
|
||||
beginstring = Opt(Rep(Any(string_prefixes + raw_prefixes)) |
|
||||
Any(char_prefixes)
|
||||
) + (Str("'") | Str('"') | Str("'''") | Str('"""'))
|
||||
two_oct = octdigit + octdigit
|
||||
three_oct = octdigit + octdigit + octdigit
|
||||
two_hex = hexdigit + hexdigit
|
||||
four_hex = two_hex + two_hex
|
||||
escapeseq = Str("\\") + (two_oct | three_oct |
|
||||
Str('N{') + Rep(AnyBut('}')) + Str('}') |
|
||||
Str('u') + four_hex | Str('x') + two_hex |
|
||||
Str('U') + four_hex + four_hex | AnyChar)
|
||||
|
||||
bra = Any("([{")
|
||||
ket = Any(")]}")
|
||||
punct = Any(":,;+-*/|&<>=.%`~^?!@")
|
||||
diphthong = Str("==", "<>", "!=", "<=", ">=", "<<", ">>", "**", "//",
|
||||
"+=", "-=", "*=", "/=", "%=", "|=", "^=", "&=",
|
||||
"<<=", ">>=", "**=", "//=", "->", "@=")
|
||||
spaces = Rep1(Any(" \t\f"))
|
||||
escaped_newline = Str("\\\n")
|
||||
lineterm = Eol + Opt(Str("\n"))
|
||||
|
||||
comment = Str("#") + Rep(AnyBut("\n"))
|
||||
|
||||
return Lexicon([
|
||||
(name, IDENT),
|
||||
(intliteral, Method('strip_underscores', symbol='INT')),
|
||||
(fltconst, Method('strip_underscores', symbol='FLOAT')),
|
||||
(imagconst, Method('strip_underscores', symbol='IMAG')),
|
||||
(punct | diphthong, TEXT),
|
||||
|
||||
(bra, Method('open_bracket_action')),
|
||||
(ket, Method('close_bracket_action')),
|
||||
(lineterm, Method('newline_action')),
|
||||
|
||||
(beginstring, Method('begin_string_action')),
|
||||
|
||||
(comment, IGNORE),
|
||||
(spaces, IGNORE),
|
||||
(escaped_newline, IGNORE),
|
||||
|
||||
State('INDENT', [
|
||||
(comment + lineterm, Method('commentline')),
|
||||
(Opt(spaces) + Opt(comment) + lineterm, IGNORE),
|
||||
(indentation, Method('indentation_action')),
|
||||
(Eof, Method('eof_action'))
|
||||
]),
|
||||
|
||||
State('SQ_STRING', [
|
||||
(escapeseq, 'ESCAPE'),
|
||||
(Rep1(AnyBut("'\"\n\\")), 'CHARS'),
|
||||
(Str('"'), 'CHARS'),
|
||||
(Str("\n"), Method('unclosed_string_action')),
|
||||
(Str("'"), Method('end_string_action')),
|
||||
(Eof, 'EOF')
|
||||
]),
|
||||
|
||||
State('DQ_STRING', [
|
||||
(escapeseq, 'ESCAPE'),
|
||||
(Rep1(AnyBut('"\n\\')), 'CHARS'),
|
||||
(Str("'"), 'CHARS'),
|
||||
(Str("\n"), Method('unclosed_string_action')),
|
||||
(Str('"'), Method('end_string_action')),
|
||||
(Eof, 'EOF')
|
||||
]),
|
||||
|
||||
State('TSQ_STRING', [
|
||||
(escapeseq, 'ESCAPE'),
|
||||
(Rep1(AnyBut("'\"\n\\")), 'CHARS'),
|
||||
(Any("'\""), 'CHARS'),
|
||||
(Str("\n"), 'NEWLINE'),
|
||||
(Str("'''"), Method('end_string_action')),
|
||||
(Eof, 'EOF')
|
||||
]),
|
||||
|
||||
State('TDQ_STRING', [
|
||||
(escapeseq, 'ESCAPE'),
|
||||
(Rep1(AnyBut('"\'\n\\')), 'CHARS'),
|
||||
(Any("'\""), 'CHARS'),
|
||||
(Str("\n"), 'NEWLINE'),
|
||||
(Str('"""'), Method('end_string_action')),
|
||||
(Eof, 'EOF')
|
||||
]),
|
||||
|
||||
(Eof, Method('eof_action'))
|
||||
],
|
||||
|
||||
# FIXME: Plex 1.9 needs different args here from Plex 1.1.4
|
||||
#debug_flags = scanner_debug_flags,
|
||||
#debug_file = scanner_dump_file
|
||||
)
|
||||
|
||||
@@ -0,0 +1,909 @@
|
||||
#
|
||||
# Cython Top Level
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import io
|
||||
|
||||
if sys.version_info[:2] < (2, 6) or (3, 0) <= sys.version_info[:2] < (3, 3):
|
||||
sys.stderr.write("Sorry, Cython requires Python 2.6+ or 3.3+, found %d.%d\n" % tuple(sys.version_info[:2]))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from __builtin__ import basestring
|
||||
except ImportError:
|
||||
basestring = str
|
||||
|
||||
# Do not import Parsing here, import it when needed, because Parsing imports
|
||||
# Nodes, which globally needs debug command line options initialized to set a
|
||||
# conditional metaclass. These options are processed by CmdLine called from
|
||||
# main() in this file.
|
||||
# import Parsing
|
||||
from . import Errors
|
||||
from .StringEncoding import EncodedString
|
||||
from .Scanning import PyrexScanner, FileSourceDescriptor
|
||||
from .Errors import PyrexError, CompileError, error, warning
|
||||
from .Symtab import ModuleScope
|
||||
from .. import Utils
|
||||
from . import Options
|
||||
|
||||
from . import Version # legacy import needed by old PyTables versions
|
||||
version = Version.version # legacy attribute - use "Cython.__version__" instead
|
||||
|
||||
module_name_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$")
|
||||
|
||||
verbose = 0
|
||||
|
||||
standard_include_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
os.path.pardir, 'Includes'))
|
||||
|
||||
class CompilationData(object):
|
||||
# Bundles the information that is passed from transform to transform.
|
||||
# (For now, this is only)
|
||||
|
||||
# While Context contains every pxd ever loaded, path information etc.,
|
||||
# this only contains the data related to a single compilation pass
|
||||
#
|
||||
# pyx ModuleNode Main code tree of this compilation.
|
||||
# pxds {string : ModuleNode} Trees for the pxds used in the pyx.
|
||||
# codewriter CCodeWriter Where to output final code.
|
||||
# options CompilationOptions
|
||||
# result CompilationResult
|
||||
pass
|
||||
|
||||
|
||||
class Context(object):
|
||||
# This class encapsulates the context needed for compiling
|
||||
# one or more Cython implementation files along with their
|
||||
# associated and imported declaration files. It includes
|
||||
# the root of the module import namespace and the list
|
||||
# of directories to search for include files.
|
||||
#
|
||||
# modules {string : ModuleScope}
|
||||
# include_directories [string]
|
||||
# future_directives [object]
|
||||
# language_level int currently 2 or 3 for Python 2/3
|
||||
|
||||
cython_scope = None
|
||||
language_level = None # warn when not set but default to Py2
|
||||
|
||||
def __init__(self, include_directories, compiler_directives, cpp=False,
|
||||
language_level=None, options=None):
|
||||
# cython_scope is a hack, set to False by subclasses, in order to break
|
||||
# an infinite loop.
|
||||
# Better code organization would fix it.
|
||||
|
||||
from . import Builtin, CythonScope
|
||||
self.modules = {"__builtin__" : Builtin.builtin_scope}
|
||||
self.cython_scope = CythonScope.create_cython_scope(self)
|
||||
self.modules["cython"] = self.cython_scope
|
||||
self.include_directories = include_directories
|
||||
self.future_directives = set()
|
||||
self.compiler_directives = compiler_directives
|
||||
self.cpp = cpp
|
||||
self.options = options
|
||||
|
||||
self.pxds = {} # full name -> node tree
|
||||
self._interned = {} # (type(value), value, *key_args) -> interned_value
|
||||
|
||||
if language_level is not None:
|
||||
self.set_language_level(language_level)
|
||||
|
||||
self.gdb_debug_outputwriter = None
|
||||
|
||||
def set_language_level(self, level):
|
||||
from .Future import print_function, unicode_literals, absolute_import, division
|
||||
future_directives = set()
|
||||
if level == '3str':
|
||||
level = 3
|
||||
else:
|
||||
level = int(level)
|
||||
if level >= 3:
|
||||
future_directives.add(unicode_literals)
|
||||
if level >= 3:
|
||||
future_directives.update([print_function, absolute_import, division])
|
||||
self.language_level = level
|
||||
self.future_directives = future_directives
|
||||
if level >= 3:
|
||||
self.modules['builtins'] = self.modules['__builtin__']
|
||||
|
||||
def intern_ustring(self, value, encoding=None):
|
||||
key = (EncodedString, value, encoding)
|
||||
try:
|
||||
return self._interned[key]
|
||||
except KeyError:
|
||||
pass
|
||||
value = EncodedString(value)
|
||||
if encoding:
|
||||
value.encoding = encoding
|
||||
self._interned[key] = value
|
||||
return value
|
||||
|
||||
def intern_value(self, value, *key):
|
||||
key = (type(value), value) + key
|
||||
try:
|
||||
return self._interned[key]
|
||||
except KeyError:
|
||||
pass
|
||||
self._interned[key] = value
|
||||
return value
|
||||
|
||||
# pipeline creation functions can now be found in Pipeline.py
|
||||
|
||||
def process_pxd(self, source_desc, scope, module_name):
|
||||
from . import Pipeline
|
||||
if isinstance(source_desc, FileSourceDescriptor) and source_desc._file_type == 'pyx':
|
||||
source = CompilationSource(source_desc, module_name, os.getcwd())
|
||||
result_sink = create_default_resultobj(source, self.options)
|
||||
pipeline = Pipeline.create_pyx_as_pxd_pipeline(self, result_sink)
|
||||
result = Pipeline.run_pipeline(pipeline, source)
|
||||
else:
|
||||
pipeline = Pipeline.create_pxd_pipeline(self, scope, module_name)
|
||||
result = Pipeline.run_pipeline(pipeline, source_desc)
|
||||
return result
|
||||
|
||||
def nonfatal_error(self, exc):
|
||||
return Errors.report_error(exc)
|
||||
|
||||
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1,
|
||||
absolute_fallback=True):
|
||||
# Finds and returns the module scope corresponding to
|
||||
# the given relative or absolute module name. If this
|
||||
# is the first time the module has been requested, finds
|
||||
# the corresponding .pxd file and process it.
|
||||
# If relative_to is not None, it must be a module scope,
|
||||
# and the module will first be searched for relative to
|
||||
# that module, provided its name is not a dotted name.
|
||||
debug_find_module = 0
|
||||
if debug_find_module:
|
||||
print("Context.find_module: module_name = %s, relative_to = %s, pos = %s, need_pxd = %s" % (
|
||||
module_name, relative_to, pos, need_pxd))
|
||||
|
||||
scope = None
|
||||
pxd_pathname = None
|
||||
if relative_to:
|
||||
if module_name:
|
||||
# from .module import ...
|
||||
qualified_name = relative_to.qualify_name(module_name)
|
||||
else:
|
||||
# from . import ...
|
||||
qualified_name = relative_to.qualified_name
|
||||
scope = relative_to
|
||||
relative_to = None
|
||||
else:
|
||||
qualified_name = module_name
|
||||
|
||||
if not module_name_pattern.match(qualified_name):
|
||||
raise CompileError(pos or (module_name, 0, 0),
|
||||
"'%s' is not a valid module name" % module_name)
|
||||
|
||||
if relative_to:
|
||||
if debug_find_module:
|
||||
print("...trying relative import")
|
||||
scope = relative_to.lookup_submodule(module_name)
|
||||
if not scope:
|
||||
pxd_pathname = self.find_pxd_file(qualified_name, pos)
|
||||
if pxd_pathname:
|
||||
scope = relative_to.find_submodule(module_name)
|
||||
if not scope:
|
||||
if debug_find_module:
|
||||
print("...trying absolute import")
|
||||
if absolute_fallback:
|
||||
qualified_name = module_name
|
||||
scope = self
|
||||
for name in qualified_name.split("."):
|
||||
scope = scope.find_submodule(name)
|
||||
|
||||
if debug_find_module:
|
||||
print("...scope = %s" % scope)
|
||||
if not scope.pxd_file_loaded:
|
||||
if debug_find_module:
|
||||
print("...pxd not loaded")
|
||||
if not pxd_pathname:
|
||||
if debug_find_module:
|
||||
print("...looking for pxd file")
|
||||
# Only look in sys.path if we are explicitly looking
|
||||
# for a .pxd file.
|
||||
pxd_pathname = self.find_pxd_file(qualified_name, pos, sys_path=need_pxd)
|
||||
if debug_find_module:
|
||||
print("......found %s" % pxd_pathname)
|
||||
if not pxd_pathname and need_pxd:
|
||||
# Set pxd_file_loaded such that we don't need to
|
||||
# look for the non-existing pxd file next time.
|
||||
scope.pxd_file_loaded = True
|
||||
package_pathname = self.search_include_directories(qualified_name, ".py", pos)
|
||||
if package_pathname and package_pathname.endswith('__init__.py'):
|
||||
pass
|
||||
else:
|
||||
error(pos, "'%s.pxd' not found" % qualified_name.replace('.', os.sep))
|
||||
if pxd_pathname:
|
||||
scope.pxd_file_loaded = True
|
||||
try:
|
||||
if debug_find_module:
|
||||
print("Context.find_module: Parsing %s" % pxd_pathname)
|
||||
rel_path = module_name.replace('.', os.sep) + os.path.splitext(pxd_pathname)[1]
|
||||
if not pxd_pathname.endswith(rel_path):
|
||||
rel_path = pxd_pathname # safety measure to prevent printing incorrect paths
|
||||
source_desc = FileSourceDescriptor(pxd_pathname, rel_path)
|
||||
err, result = self.process_pxd(source_desc, scope, qualified_name)
|
||||
if err:
|
||||
raise err
|
||||
(pxd_codenodes, pxd_scope) = result
|
||||
self.pxds[module_name] = (pxd_codenodes, pxd_scope)
|
||||
except CompileError:
|
||||
pass
|
||||
return scope
|
||||
|
||||
def find_pxd_file(self, qualified_name, pos, sys_path=True):
|
||||
# Search include path (and sys.path if sys_path is True) for
|
||||
# the .pxd file corresponding to the given fully-qualified
|
||||
# module name.
|
||||
# Will find either a dotted filename or a file in a
|
||||
# package directory. If a source file position is given,
|
||||
# the directory containing the source file is searched first
|
||||
# for a dotted filename, and its containing package root
|
||||
# directory is searched first for a non-dotted filename.
|
||||
pxd = self.search_include_directories(qualified_name, ".pxd", pos, sys_path=sys_path)
|
||||
if pxd is None: # XXX Keep this until Includes/Deprecated is removed
|
||||
if (qualified_name.startswith('python') or
|
||||
qualified_name in ('stdlib', 'stdio', 'stl')):
|
||||
standard_include_path = os.path.abspath(os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
|
||||
deprecated_include_path = os.path.join(standard_include_path, 'Deprecated')
|
||||
self.include_directories.append(deprecated_include_path)
|
||||
try:
|
||||
pxd = self.search_include_directories(qualified_name, ".pxd", pos)
|
||||
finally:
|
||||
self.include_directories.pop()
|
||||
if pxd:
|
||||
name = qualified_name
|
||||
if name.startswith('python'):
|
||||
warning(pos, "'%s' is deprecated, use 'cpython'" % name, 1)
|
||||
elif name in ('stdlib', 'stdio'):
|
||||
warning(pos, "'%s' is deprecated, use 'libc.%s'" % (name, name), 1)
|
||||
elif name in ('stl'):
|
||||
warning(pos, "'%s' is deprecated, use 'libcpp.*.*'" % name, 1)
|
||||
if pxd is None and Options.cimport_from_pyx:
|
||||
return self.find_pyx_file(qualified_name, pos)
|
||||
return pxd
|
||||
|
||||
def find_pyx_file(self, qualified_name, pos):
|
||||
# Search include path for the .pyx file corresponding to the
|
||||
# given fully-qualified module name, as for find_pxd_file().
|
||||
return self.search_include_directories(qualified_name, ".pyx", pos)
|
||||
|
||||
def find_include_file(self, filename, pos):
|
||||
# Search list of include directories for filename.
|
||||
# Reports an error and returns None if not found.
|
||||
path = self.search_include_directories(filename, "", pos,
|
||||
include=True)
|
||||
if not path:
|
||||
error(pos, "'%s' not found" % filename)
|
||||
return path
|
||||
|
||||
def search_include_directories(self, qualified_name, suffix, pos,
|
||||
include=False, sys_path=False):
|
||||
include_dirs = self.include_directories
|
||||
if sys_path:
|
||||
include_dirs = include_dirs + sys.path
|
||||
# include_dirs must be hashable for caching in @cached_function
|
||||
include_dirs = tuple(include_dirs + [standard_include_path])
|
||||
return search_include_directories(include_dirs, qualified_name,
|
||||
suffix, pos, include)
|
||||
|
||||
def find_root_package_dir(self, file_path):
|
||||
return Utils.find_root_package_dir(file_path)
|
||||
|
||||
def check_package_dir(self, dir, package_names):
|
||||
return Utils.check_package_dir(dir, tuple(package_names))
|
||||
|
||||
def c_file_out_of_date(self, source_path, output_path):
|
||||
if not os.path.exists(output_path):
|
||||
return 1
|
||||
c_time = Utils.modification_time(output_path)
|
||||
if Utils.file_newer_than(source_path, c_time):
|
||||
return 1
|
||||
pos = [source_path]
|
||||
pxd_path = Utils.replace_suffix(source_path, ".pxd")
|
||||
if os.path.exists(pxd_path) and Utils.file_newer_than(pxd_path, c_time):
|
||||
return 1
|
||||
for kind, name in self.read_dependency_file(source_path):
|
||||
if kind == "cimport":
|
||||
dep_path = self.find_pxd_file(name, pos)
|
||||
elif kind == "include":
|
||||
dep_path = self.search_include_directories(name, pos)
|
||||
else:
|
||||
continue
|
||||
if dep_path and Utils.file_newer_than(dep_path, c_time):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def find_cimported_module_names(self, source_path):
|
||||
return [ name for kind, name in self.read_dependency_file(source_path)
|
||||
if kind == "cimport" ]
|
||||
|
||||
def is_package_dir(self, dir_path):
|
||||
return Utils.is_package_dir(dir_path)
|
||||
|
||||
def read_dependency_file(self, source_path):
|
||||
dep_path = Utils.replace_suffix(source_path, ".dep")
|
||||
if os.path.exists(dep_path):
|
||||
f = open(dep_path, "rU")
|
||||
chunks = [ line.strip().split(" ", 1)
|
||||
for line in f.readlines()
|
||||
if " " in line.strip() ]
|
||||
f.close()
|
||||
return chunks
|
||||
else:
|
||||
return ()
|
||||
|
||||
def lookup_submodule(self, name):
|
||||
# Look up a top-level module. Returns None if not found.
|
||||
return self.modules.get(name, None)
|
||||
|
||||
def find_submodule(self, name):
|
||||
# Find a top-level module, creating a new one if needed.
|
||||
scope = self.lookup_submodule(name)
|
||||
if not scope:
|
||||
scope = ModuleScope(name,
|
||||
parent_module = None, context = self)
|
||||
self.modules[name] = scope
|
||||
return scope
|
||||
|
||||
def parse(self, source_desc, scope, pxd, full_module_name):
|
||||
if not isinstance(source_desc, FileSourceDescriptor):
|
||||
raise RuntimeError("Only file sources for code supported")
|
||||
source_filename = source_desc.filename
|
||||
scope.cpp = self.cpp
|
||||
# Parse the given source file and return a parse tree.
|
||||
num_errors = Errors.num_errors
|
||||
try:
|
||||
with Utils.open_source_file(source_filename) as f:
|
||||
from . import Parsing
|
||||
s = PyrexScanner(f, source_desc, source_encoding = f.encoding,
|
||||
scope = scope, context = self)
|
||||
tree = Parsing.p_module(s, pxd, full_module_name)
|
||||
if self.options.formal_grammar:
|
||||
try:
|
||||
from ..Parser import ConcreteSyntaxTree
|
||||
except ImportError:
|
||||
raise RuntimeError(
|
||||
"Formal grammar can only be used with compiled Cython with an available pgen.")
|
||||
ConcreteSyntaxTree.p_module(source_filename)
|
||||
except UnicodeDecodeError as e:
|
||||
#import traceback
|
||||
#traceback.print_exc()
|
||||
raise self._report_decode_error(source_desc, e)
|
||||
|
||||
if Errors.num_errors > num_errors:
|
||||
raise CompileError()
|
||||
return tree
|
||||
|
||||
def _report_decode_error(self, source_desc, exc):
|
||||
msg = exc.args[-1]
|
||||
position = exc.args[2]
|
||||
encoding = exc.args[0]
|
||||
|
||||
line = 1
|
||||
column = idx = 0
|
||||
with io.open(source_desc.filename, "r", encoding='iso8859-1', newline='') as f:
|
||||
for line, data in enumerate(f, 1):
|
||||
idx += len(data)
|
||||
if idx >= position:
|
||||
column = position - (idx - len(data)) + 1
|
||||
break
|
||||
|
||||
return error((source_desc, line, column),
|
||||
"Decoding error, missing or incorrect coding=<encoding-name> "
|
||||
"at top of source (cannot decode with encoding %r: %s)" % (encoding, msg))
|
||||
|
||||
def extract_module_name(self, path, options):
|
||||
# Find fully_qualified module name from the full pathname
|
||||
# of a source file.
|
||||
dir, filename = os.path.split(path)
|
||||
module_name, _ = os.path.splitext(filename)
|
||||
if "." in module_name:
|
||||
return module_name
|
||||
names = [module_name]
|
||||
while self.is_package_dir(dir):
|
||||
parent, package_name = os.path.split(dir)
|
||||
if parent == dir:
|
||||
break
|
||||
names.append(package_name)
|
||||
dir = parent
|
||||
names.reverse()
|
||||
return ".".join(names)
|
||||
|
||||
def setup_errors(self, options, result):
|
||||
Errors.reset() # clear any remaining error state
|
||||
if options.use_listing_file:
|
||||
path = result.listing_file = Utils.replace_suffix(result.main_source_file, ".lis")
|
||||
else:
|
||||
path = None
|
||||
Errors.open_listing_file(path=path,
|
||||
echo_to_stderr=options.errors_to_stderr)
|
||||
|
||||
def teardown_errors(self, err, options, result):
|
||||
source_desc = result.compilation_source.source_desc
|
||||
if not isinstance(source_desc, FileSourceDescriptor):
|
||||
raise RuntimeError("Only file sources for code supported")
|
||||
Errors.close_listing_file()
|
||||
result.num_errors = Errors.num_errors
|
||||
if result.num_errors > 0:
|
||||
err = True
|
||||
if err and result.c_file:
|
||||
try:
|
||||
Utils.castrate_file(result.c_file, os.stat(source_desc.filename))
|
||||
except EnvironmentError:
|
||||
pass
|
||||
result.c_file = None
|
||||
|
||||
|
||||
def get_output_filename(source_filename, cwd, options):
|
||||
if options.cplus:
|
||||
c_suffix = ".cpp"
|
||||
else:
|
||||
c_suffix = ".c"
|
||||
suggested_file_name = Utils.replace_suffix(source_filename, c_suffix)
|
||||
if options.output_file:
|
||||
out_path = os.path.join(cwd, options.output_file)
|
||||
if os.path.isdir(out_path):
|
||||
return os.path.join(out_path, os.path.basename(suggested_file_name))
|
||||
else:
|
||||
return out_path
|
||||
else:
|
||||
return suggested_file_name
|
||||
|
||||
|
||||
def create_default_resultobj(compilation_source, options):
|
||||
result = CompilationResult()
|
||||
result.main_source_file = compilation_source.source_desc.filename
|
||||
result.compilation_source = compilation_source
|
||||
source_desc = compilation_source.source_desc
|
||||
result.c_file = get_output_filename(source_desc.filename,
|
||||
compilation_source.cwd, options)
|
||||
result.embedded_metadata = options.embedded_metadata
|
||||
return result
|
||||
|
||||
|
||||
def run_pipeline(source, options, full_module_name=None, context=None):
|
||||
from . import Pipeline
|
||||
|
||||
source_ext = os.path.splitext(source)[1]
|
||||
options.configure_language_defaults(source_ext[1:]) # py/pyx
|
||||
if context is None:
|
||||
context = options.create_context()
|
||||
|
||||
# Set up source object
|
||||
cwd = os.getcwd()
|
||||
abs_path = os.path.abspath(source)
|
||||
full_module_name = full_module_name or context.extract_module_name(source, options)
|
||||
|
||||
Utils.raise_error_if_module_name_forbidden(full_module_name)
|
||||
|
||||
if options.relative_path_in_code_position_comments:
|
||||
rel_path = full_module_name.replace('.', os.sep) + source_ext
|
||||
if not abs_path.endswith(rel_path):
|
||||
rel_path = source # safety measure to prevent printing incorrect paths
|
||||
else:
|
||||
rel_path = abs_path
|
||||
source_desc = FileSourceDescriptor(abs_path, rel_path)
|
||||
source = CompilationSource(source_desc, full_module_name, cwd)
|
||||
|
||||
# Set up result object
|
||||
result = create_default_resultobj(source, options)
|
||||
|
||||
if options.annotate is None:
|
||||
# By default, decide based on whether an html file already exists.
|
||||
html_filename = os.path.splitext(result.c_file)[0] + ".html"
|
||||
if os.path.exists(html_filename):
|
||||
with io.open(html_filename, "r", encoding="UTF-8") as html_file:
|
||||
if u'<!-- Generated by Cython' in html_file.read(100):
|
||||
options.annotate = True
|
||||
|
||||
# Get pipeline
|
||||
if source_ext.lower() == '.py' or not source_ext:
|
||||
pipeline = Pipeline.create_py_pipeline(context, options, result)
|
||||
else:
|
||||
pipeline = Pipeline.create_pyx_pipeline(context, options, result)
|
||||
|
||||
context.setup_errors(options, result)
|
||||
err, enddata = Pipeline.run_pipeline(pipeline, source)
|
||||
context.teardown_errors(err, options, result)
|
||||
return result
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
#
|
||||
# Main Python entry points
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
class CompilationSource(object):
|
||||
"""
|
||||
Contains the data necessary to start up a compilation pipeline for
|
||||
a single compilation unit.
|
||||
"""
|
||||
def __init__(self, source_desc, full_module_name, cwd):
|
||||
self.source_desc = source_desc
|
||||
self.full_module_name = full_module_name
|
||||
self.cwd = cwd
|
||||
|
||||
|
||||
class CompilationOptions(object):
|
||||
r"""
|
||||
See default_options at the end of this module for a list of all possible
|
||||
options and CmdLine.usage and CmdLine.parse_command_line() for their
|
||||
meaning.
|
||||
"""
|
||||
def __init__(self, defaults=None, **kw):
|
||||
self.include_path = []
|
||||
if defaults:
|
||||
if isinstance(defaults, CompilationOptions):
|
||||
defaults = defaults.__dict__
|
||||
else:
|
||||
defaults = default_options
|
||||
|
||||
options = dict(defaults)
|
||||
options.update(kw)
|
||||
|
||||
# let's assume 'default_options' contains a value for most known compiler options
|
||||
# and validate against them
|
||||
unknown_options = set(options) - set(default_options)
|
||||
# ignore valid options that are not in the defaults
|
||||
unknown_options.difference_update(['include_path'])
|
||||
if unknown_options:
|
||||
message = "got unknown compilation option%s, please remove: %s" % (
|
||||
's' if len(unknown_options) > 1 else '',
|
||||
', '.join(unknown_options))
|
||||
raise ValueError(message)
|
||||
|
||||
directive_defaults = Options.get_directive_defaults()
|
||||
directives = dict(options['compiler_directives']) # copy mutable field
|
||||
# check for invalid directives
|
||||
unknown_directives = set(directives) - set(directive_defaults)
|
||||
if unknown_directives:
|
||||
message = "got unknown compiler directive%s: %s" % (
|
||||
's' if len(unknown_directives) > 1 else '',
|
||||
', '.join(unknown_directives))
|
||||
raise ValueError(message)
|
||||
options['compiler_directives'] = directives
|
||||
if directives.get('np_pythran', False) and not options['cplus']:
|
||||
import warnings
|
||||
warnings.warn("C++ mode forced when in Pythran mode!")
|
||||
options['cplus'] = True
|
||||
if 'language_level' in directives and 'language_level' not in kw:
|
||||
options['language_level'] = directives['language_level']
|
||||
elif not options.get('language_level'):
|
||||
options['language_level'] = directive_defaults.get('language_level')
|
||||
if 'formal_grammar' in directives and 'formal_grammar' not in kw:
|
||||
options['formal_grammar'] = directives['formal_grammar']
|
||||
if options['cache'] is True:
|
||||
options['cache'] = os.path.join(Utils.get_cython_cache_dir(), 'compiler')
|
||||
|
||||
self.__dict__.update(options)
|
||||
|
||||
def configure_language_defaults(self, source_extension):
|
||||
if source_extension == 'py':
|
||||
if self.compiler_directives.get('binding') is None:
|
||||
self.compiler_directives['binding'] = True
|
||||
|
||||
def create_context(self):
|
||||
return Context(self.include_path, self.compiler_directives,
|
||||
self.cplus, self.language_level, options=self)
|
||||
|
||||
def get_fingerprint(self):
|
||||
r"""
|
||||
Return a string that contains all the options that are relevant for cache invalidation.
|
||||
"""
|
||||
# Collect only the data that can affect the generated file(s).
|
||||
data = {}
|
||||
|
||||
for key, value in self.__dict__.items():
|
||||
if key in ['show_version', 'errors_to_stderr', 'verbose', 'quiet']:
|
||||
# verbosity flags have no influence on the compilation result
|
||||
continue
|
||||
elif key in ['output_file', 'output_dir']:
|
||||
# ignore the exact name of the output file
|
||||
continue
|
||||
elif key in ['timestamps']:
|
||||
# the cache cares about the content of files, not about the timestamps of sources
|
||||
continue
|
||||
elif key in ['cache']:
|
||||
# hopefully caching has no influence on the compilation result
|
||||
continue
|
||||
elif key in ['compiler_directives']:
|
||||
# directives passed on to the C compiler do not influence the generated C code
|
||||
continue
|
||||
elif key in ['include_path']:
|
||||
# this path changes which headers are tracked as dependencies,
|
||||
# it has no influence on the generated C code
|
||||
continue
|
||||
elif key in ['working_path']:
|
||||
# this path changes where modules and pxd files are found;
|
||||
# their content is part of the fingerprint anyway, their
|
||||
# absolute path does not matter
|
||||
continue
|
||||
elif key in ['create_extension']:
|
||||
# create_extension() has already mangled the options, e.g.,
|
||||
# embedded_metadata, when the fingerprint is computed so we
|
||||
# ignore it here.
|
||||
continue
|
||||
elif key in ['build_dir']:
|
||||
# the (temporary) directory where we collect dependencies
|
||||
# has no influence on the C output
|
||||
continue
|
||||
elif key in ['use_listing_file', 'generate_pxi', 'annotate', 'annotate_coverage_xml']:
|
||||
# all output files are contained in the cache so the types of
|
||||
# files generated must be part of the fingerprint
|
||||
data[key] = value
|
||||
elif key in ['formal_grammar', 'evaluate_tree_assertions']:
|
||||
# these bits can change whether compilation to C passes/fails
|
||||
data[key] = value
|
||||
elif key in ['embedded_metadata', 'emit_linenums', 'c_line_in_traceback', 'gdb_debug', 'relative_path_in_code_position_comments']:
|
||||
# the generated code contains additional bits when these are set
|
||||
data[key] = value
|
||||
elif key in ['cplus', 'language_level', 'compile_time_env', 'np_pythran']:
|
||||
# assorted bits that, e.g., influence the parser
|
||||
data[key] = value
|
||||
elif key == ['capi_reexport_cincludes']:
|
||||
if self.capi_reexport_cincludes:
|
||||
# our caching implementation does not yet include fingerprints of all the header files
|
||||
raise NotImplementedError('capi_reexport_cincludes is not compatible with Cython caching')
|
||||
elif key == ['common_utility_include_dir']:
|
||||
if self.common_utility_include_dir:
|
||||
raise NotImplementedError('common_utility_include_dir is not compatible with Cython caching yet')
|
||||
else:
|
||||
# any unexpected option should go into the fingerprint; it's better
|
||||
# to recompile than to return incorrect results from the cache.
|
||||
data[key] = value
|
||||
|
||||
def to_fingerprint(item):
|
||||
r"""
|
||||
Recursively turn item into a string, turning dicts into lists with
|
||||
deterministic ordering.
|
||||
"""
|
||||
if isinstance(item, dict):
|
||||
item = sorted([(repr(key), to_fingerprint(value)) for key, value in item.items()])
|
||||
return repr(item)
|
||||
|
||||
return to_fingerprint(data)
|
||||
|
||||
|
||||
class CompilationResult(object):
|
||||
"""
|
||||
Results from the Cython compiler:
|
||||
|
||||
c_file string or None The generated C source file
|
||||
h_file string or None The generated C header file
|
||||
i_file string or None The generated .pxi file
|
||||
api_file string or None The generated C API .h file
|
||||
listing_file string or None File of error messages
|
||||
object_file string or None Result of compiling the C file
|
||||
extension_file string or None Result of linking the object file
|
||||
num_errors integer Number of compilation errors
|
||||
compilation_source CompilationSource
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.c_file = None
|
||||
self.h_file = None
|
||||
self.i_file = None
|
||||
self.api_file = None
|
||||
self.listing_file = None
|
||||
self.object_file = None
|
||||
self.extension_file = None
|
||||
self.main_source_file = None
|
||||
|
||||
|
||||
class CompilationResultSet(dict):
|
||||
"""
|
||||
Results from compiling multiple Pyrex source files. A mapping
|
||||
from source file paths to CompilationResult instances. Also
|
||||
has the following attributes:
|
||||
|
||||
num_errors integer Total number of compilation errors
|
||||
"""
|
||||
|
||||
num_errors = 0
|
||||
|
||||
def add(self, source, result):
|
||||
self[source] = result
|
||||
self.num_errors += result.num_errors
|
||||
|
||||
|
||||
def compile_single(source, options, full_module_name = None):
|
||||
"""
|
||||
compile_single(source, options, full_module_name)
|
||||
|
||||
Compile the given Pyrex implementation file and return a CompilationResult.
|
||||
Always compiles a single file; does not perform timestamp checking or
|
||||
recursion.
|
||||
"""
|
||||
return run_pipeline(source, options, full_module_name)
|
||||
|
||||
|
||||
def compile_multiple(sources, options):
|
||||
"""
|
||||
compile_multiple(sources, options)
|
||||
|
||||
Compiles the given sequence of Pyrex implementation files and returns
|
||||
a CompilationResultSet. Performs timestamp checking and/or recursion
|
||||
if these are specified in the options.
|
||||
"""
|
||||
if options.module_name and len(sources) > 1:
|
||||
raise RuntimeError('Full module name can only be set '
|
||||
'for single source compilation')
|
||||
# run_pipeline creates the context
|
||||
# context = options.create_context()
|
||||
sources = [os.path.abspath(source) for source in sources]
|
||||
processed = set()
|
||||
results = CompilationResultSet()
|
||||
timestamps = options.timestamps
|
||||
verbose = options.verbose
|
||||
context = None
|
||||
cwd = os.getcwd()
|
||||
for source in sources:
|
||||
if source not in processed:
|
||||
if context is None:
|
||||
context = options.create_context()
|
||||
output_filename = get_output_filename(source, cwd, options)
|
||||
out_of_date = context.c_file_out_of_date(source, output_filename)
|
||||
if (not timestamps) or out_of_date:
|
||||
if verbose:
|
||||
sys.stderr.write("Compiling %s\n" % source)
|
||||
result = run_pipeline(source, options,
|
||||
full_module_name=options.module_name,
|
||||
context=context)
|
||||
results.add(source, result)
|
||||
# Compiling multiple sources in one context doesn't quite
|
||||
# work properly yet.
|
||||
context = None
|
||||
processed.add(source)
|
||||
return results
|
||||
|
||||
|
||||
def compile(source, options = None, full_module_name = None, **kwds):
|
||||
"""
|
||||
compile(source [, options], [, <option> = <value>]...)
|
||||
|
||||
Compile one or more Pyrex implementation files, with optional timestamp
|
||||
checking and recursing on dependencies. The source argument may be a string
|
||||
or a sequence of strings. If it is a string and no recursion or timestamp
|
||||
checking is requested, a CompilationResult is returned, otherwise a
|
||||
CompilationResultSet is returned.
|
||||
"""
|
||||
options = CompilationOptions(defaults = options, **kwds)
|
||||
if isinstance(source, basestring) and not options.timestamps:
|
||||
return compile_single(source, options, full_module_name)
|
||||
else:
|
||||
return compile_multiple(source, options)
|
||||
|
||||
|
||||
@Utils.cached_function
|
||||
def search_include_directories(dirs, qualified_name, suffix, pos, include=False):
|
||||
"""
|
||||
Search the list of include directories for the given file name.
|
||||
|
||||
If a source file position is given, first searches the directory
|
||||
containing that file. Returns None if not found, but does not
|
||||
report an error.
|
||||
|
||||
The 'include' option will disable package dereferencing.
|
||||
"""
|
||||
|
||||
if pos:
|
||||
file_desc = pos[0]
|
||||
if not isinstance(file_desc, FileSourceDescriptor):
|
||||
raise RuntimeError("Only file sources for code supported")
|
||||
if include:
|
||||
dirs = (os.path.dirname(file_desc.filename),) + dirs
|
||||
else:
|
||||
dirs = (Utils.find_root_package_dir(file_desc.filename),) + dirs
|
||||
|
||||
dotted_filename = qualified_name
|
||||
if suffix:
|
||||
dotted_filename += suffix
|
||||
|
||||
if not include:
|
||||
names = qualified_name.split('.')
|
||||
package_names = tuple(names[:-1])
|
||||
module_name = names[-1]
|
||||
module_filename = module_name + suffix
|
||||
package_filename = "__init__" + suffix
|
||||
|
||||
for dirname in dirs:
|
||||
path = os.path.join(dirname, dotted_filename)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
|
||||
if not include:
|
||||
package_dir = Utils.check_package_dir(dirname, package_names)
|
||||
if package_dir is not None:
|
||||
path = os.path.join(package_dir, module_filename)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
path = os.path.join(package_dir, module_name,
|
||||
package_filename)
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
#
|
||||
# Main command-line entry point
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
def setuptools_main():
|
||||
return main(command_line = 1)
|
||||
|
||||
|
||||
def main(command_line = 0):
|
||||
args = sys.argv[1:]
|
||||
any_failures = 0
|
||||
if command_line:
|
||||
from .CmdLine import parse_command_line
|
||||
options, sources = parse_command_line(args)
|
||||
else:
|
||||
options = CompilationOptions(default_options)
|
||||
sources = args
|
||||
|
||||
if options.show_version:
|
||||
sys.stderr.write("Cython version %s\n" % version)
|
||||
if options.working_path!="":
|
||||
os.chdir(options.working_path)
|
||||
try:
|
||||
result = compile(sources, options)
|
||||
if result.num_errors > 0:
|
||||
any_failures = 1
|
||||
except (EnvironmentError, PyrexError) as e:
|
||||
sys.stderr.write(str(e) + '\n')
|
||||
any_failures = 1
|
||||
if any_failures:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
#
|
||||
# Set the default options depending on the platform
|
||||
#
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
default_options = dict(
|
||||
show_version = 0,
|
||||
use_listing_file = 0,
|
||||
errors_to_stderr = 1,
|
||||
cplus = 0,
|
||||
output_file = None,
|
||||
annotate = None,
|
||||
annotate_coverage_xml = None,
|
||||
generate_pxi = 0,
|
||||
capi_reexport_cincludes = 0,
|
||||
working_path = "",
|
||||
timestamps = None,
|
||||
verbose = 0,
|
||||
quiet = 0,
|
||||
compiler_directives = {},
|
||||
embedded_metadata = {},
|
||||
evaluate_tree_assertions = False,
|
||||
emit_linenums = False,
|
||||
relative_path_in_code_position_comments = True,
|
||||
c_line_in_traceback = True,
|
||||
language_level = None, # warn but default to 2
|
||||
formal_grammar = False,
|
||||
gdb_debug = False,
|
||||
compile_time_env = None,
|
||||
common_utility_include_dir = None,
|
||||
output_dir=None,
|
||||
build_dir=None,
|
||||
cache=None,
|
||||
create_extension=None,
|
||||
module_name=None,
|
||||
np_pythran=False
|
||||
)
|
||||
@@ -0,0 +1,858 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Errors import CompileError, error
|
||||
from . import ExprNodes
|
||||
from .ExprNodes import IntNode, NameNode, AttributeNode
|
||||
from . import Options
|
||||
from .Code import UtilityCode, TempitaUtilityCode
|
||||
from .UtilityCode import CythonUtilityCode
|
||||
from . import Buffer
|
||||
from . import PyrexTypes
|
||||
from . import ModuleNode
|
||||
|
||||
START_ERR = "Start must not be given."
|
||||
STOP_ERR = "Axis specification only allowed in the 'step' slot."
|
||||
STEP_ERR = "Step must be omitted, 1, or a valid specifier."
|
||||
BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous."
|
||||
INVALID_ERR = "Invalid axis specification."
|
||||
NOT_CIMPORTED_ERR = "Variable was not cimported from cython.view"
|
||||
EXPR_ERR = "no expressions allowed in axis spec, only names and literals."
|
||||
CF_ERR = "Invalid axis specification for a C/Fortran contiguous array."
|
||||
ERR_UNINITIALIZED = ("Cannot check if memoryview %s is initialized without the "
|
||||
"GIL, consider using initializedcheck(False)")
|
||||
|
||||
|
||||
def concat_flags(*flags):
|
||||
return "(%s)" % "|".join(flags)
|
||||
|
||||
|
||||
format_flag = "PyBUF_FORMAT"
|
||||
|
||||
memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)"
|
||||
memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)"
|
||||
memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)"
|
||||
memview_full_access = "PyBUF_FULL_RO"
|
||||
#memview_strided_access = "PyBUF_STRIDED_RO"
|
||||
memview_strided_access = "PyBUF_RECORDS_RO"
|
||||
|
||||
MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT'
|
||||
MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR'
|
||||
MEMVIEW_FULL = '__Pyx_MEMVIEW_FULL'
|
||||
MEMVIEW_CONTIG = '__Pyx_MEMVIEW_CONTIG'
|
||||
MEMVIEW_STRIDED= '__Pyx_MEMVIEW_STRIDED'
|
||||
MEMVIEW_FOLLOW = '__Pyx_MEMVIEW_FOLLOW'
|
||||
|
||||
_spec_to_const = {
|
||||
'direct' : MEMVIEW_DIRECT,
|
||||
'ptr' : MEMVIEW_PTR,
|
||||
'full' : MEMVIEW_FULL,
|
||||
'contig' : MEMVIEW_CONTIG,
|
||||
'strided': MEMVIEW_STRIDED,
|
||||
'follow' : MEMVIEW_FOLLOW,
|
||||
}
|
||||
|
||||
_spec_to_abbrev = {
|
||||
'direct' : 'd',
|
||||
'ptr' : 'p',
|
||||
'full' : 'f',
|
||||
'contig' : 'c',
|
||||
'strided' : 's',
|
||||
'follow' : '_',
|
||||
}
|
||||
|
||||
memslice_entry_init = "{ 0, 0, { 0 }, { 0 }, { 0 } }"
|
||||
|
||||
memview_name = u'memoryview'
|
||||
memview_typeptr_cname = '__pyx_memoryview_type'
|
||||
memview_objstruct_cname = '__pyx_memoryview_obj'
|
||||
memviewslice_cname = u'__Pyx_memviewslice'
|
||||
|
||||
|
||||
def put_init_entry(mv_cname, code):
|
||||
code.putln("%s.data = NULL;" % mv_cname)
|
||||
code.putln("%s.memview = NULL;" % mv_cname)
|
||||
|
||||
|
||||
#def axes_to_str(axes):
|
||||
# return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
|
||||
|
||||
|
||||
def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
|
||||
have_gil=False, first_assignment=True):
|
||||
"We can avoid decreffing the lhs if we know it is the first assignment"
|
||||
assert rhs.type.is_memoryviewslice
|
||||
|
||||
pretty_rhs = rhs.result_in_temp() or rhs.is_simple()
|
||||
if pretty_rhs:
|
||||
rhstmp = rhs.result()
|
||||
else:
|
||||
rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
|
||||
code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
|
||||
|
||||
# Allow uninitialized assignment
|
||||
#code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
|
||||
put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code,
|
||||
have_gil=have_gil, first_assignment=first_assignment)
|
||||
|
||||
if not pretty_rhs:
|
||||
code.funcstate.release_temp(rhstmp)
|
||||
|
||||
|
||||
def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code,
|
||||
have_gil=False, first_assignment=False):
|
||||
if not first_assignment:
|
||||
code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil)
|
||||
|
||||
if not rhs.result_in_temp():
|
||||
rhs.make_owned_memoryviewslice(code)
|
||||
|
||||
code.putln("%s = %s;" % (lhs_cname, rhs_cname))
|
||||
|
||||
|
||||
def get_buf_flags(specs):
|
||||
is_c_contig, is_f_contig = is_cf_contig(specs)
|
||||
|
||||
if is_c_contig:
|
||||
return memview_c_contiguous
|
||||
elif is_f_contig:
|
||||
return memview_f_contiguous
|
||||
|
||||
access, packing = zip(*specs)
|
||||
|
||||
if 'full' in access or 'ptr' in access:
|
||||
return memview_full_access
|
||||
else:
|
||||
return memview_strided_access
|
||||
|
||||
|
||||
def insert_newaxes(memoryviewtype, n):
|
||||
axes = [('direct', 'strided')] * n
|
||||
axes.extend(memoryviewtype.axes)
|
||||
return PyrexTypes.MemoryViewSliceType(memoryviewtype.dtype, axes)
|
||||
|
||||
|
||||
def broadcast_types(src, dst):
|
||||
n = abs(src.ndim - dst.ndim)
|
||||
if src.ndim < dst.ndim:
|
||||
return insert_newaxes(src, n), dst
|
||||
else:
|
||||
return src, insert_newaxes(dst, n)
|
||||
|
||||
|
||||
def valid_memslice_dtype(dtype, i=0):
|
||||
"""
|
||||
Return whether type dtype can be used as the base type of a
|
||||
memoryview slice.
|
||||
|
||||
We support structs, numeric types and objects
|
||||
"""
|
||||
if dtype.is_complex and dtype.real_type.is_int:
|
||||
return False
|
||||
|
||||
if dtype is PyrexTypes.c_bint_type:
|
||||
return False
|
||||
|
||||
if dtype.is_struct and dtype.kind == 'struct':
|
||||
for member in dtype.scope.var_entries:
|
||||
if not valid_memslice_dtype(member.type):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
return (
|
||||
dtype.is_error or
|
||||
# Pointers are not valid (yet)
|
||||
# (dtype.is_ptr and valid_memslice_dtype(dtype.base_type)) or
|
||||
(dtype.is_array and i < 8 and
|
||||
valid_memslice_dtype(dtype.base_type, i + 1)) or
|
||||
dtype.is_numeric or
|
||||
dtype.is_pyobject or
|
||||
dtype.is_fused or # accept this as it will be replaced by specializations later
|
||||
(dtype.is_typedef and valid_memslice_dtype(dtype.typedef_base_type))
|
||||
)
|
||||
|
||||
|
||||
class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
|
||||
"""
|
||||
May be used during code generation time to be queried for
|
||||
shape/strides/suboffsets attributes, or to perform indexing or slicing.
|
||||
"""
|
||||
def __init__(self, entry):
|
||||
self.entry = entry
|
||||
self.type = entry.type
|
||||
self.cname = entry.cname
|
||||
|
||||
self.buf_ptr = "%s.data" % self.cname
|
||||
|
||||
dtype = self.entry.type.dtype
|
||||
self.buf_ptr_type = PyrexTypes.CPtrType(dtype)
|
||||
self.init_attributes()
|
||||
|
||||
def get_buf_suboffsetvars(self):
|
||||
return self._for_all_ndim("%s.suboffsets[%d]")
|
||||
|
||||
def get_buf_stridevars(self):
|
||||
return self._for_all_ndim("%s.strides[%d]")
|
||||
|
||||
def get_buf_shapevars(self):
|
||||
return self._for_all_ndim("%s.shape[%d]")
|
||||
|
||||
def generate_buffer_lookup_code(self, code, index_cnames):
|
||||
axes = [(dim, index_cnames[dim], access, packing)
|
||||
for dim, (access, packing) in enumerate(self.type.axes)]
|
||||
return self._generate_buffer_lookup_code(code, axes)
|
||||
|
||||
def _generate_buffer_lookup_code(self, code, axes, cast_result=True):
|
||||
"""
|
||||
Generate a single expression that indexes the memory view slice
|
||||
in each dimension.
|
||||
"""
|
||||
bufp = self.buf_ptr
|
||||
type_decl = self.type.dtype.empty_declaration_code()
|
||||
|
||||
for dim, index, access, packing in axes:
|
||||
shape = "%s.shape[%d]" % (self.cname, dim)
|
||||
stride = "%s.strides[%d]" % (self.cname, dim)
|
||||
suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
|
||||
|
||||
flag = get_memoryview_flag(access, packing)
|
||||
|
||||
if flag in ("generic", "generic_contiguous"):
|
||||
# Note: we cannot do cast tricks to avoid stride multiplication
|
||||
# for generic_contiguous, as we may have to do (dtype *)
|
||||
# or (dtype **) arithmetic, we won't know which unless
|
||||
# we check suboffsets
|
||||
code.globalstate.use_utility_code(memviewslice_index_helpers)
|
||||
bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
|
||||
(bufp, index, stride, suboffset))
|
||||
|
||||
elif flag == "indirect":
|
||||
bufp = "(%s + %s * %s)" % (bufp, index, stride)
|
||||
bufp = ("(*((char **) %s) + %s)" % (bufp, suboffset))
|
||||
|
||||
elif flag == "indirect_contiguous":
|
||||
# Note: we do char ** arithmetic
|
||||
bufp = "(*((char **) %s + %s) + %s)" % (bufp, index, suboffset)
|
||||
|
||||
elif flag == "strided":
|
||||
bufp = "(%s + %s * %s)" % (bufp, index, stride)
|
||||
|
||||
else:
|
||||
assert flag == 'contiguous', flag
|
||||
bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
|
||||
|
||||
bufp = '( /* dim=%d */ %s )' % (dim, bufp)
|
||||
|
||||
if cast_result:
|
||||
return "((%s *) %s)" % (type_decl, bufp)
|
||||
|
||||
return bufp
|
||||
|
||||
def generate_buffer_slice_code(self, code, indices, dst, have_gil,
|
||||
have_slices, directives):
|
||||
"""
|
||||
Slice a memoryviewslice.
|
||||
|
||||
indices - list of index nodes. If not a SliceNode, or NoneNode,
|
||||
then it must be coercible to Py_ssize_t
|
||||
|
||||
Simply call __pyx_memoryview_slice_memviewslice with the right
|
||||
arguments, unless the dimension is omitted or a bare ':', in which
|
||||
case we copy over the shape/strides/suboffsets attributes directly
|
||||
for that dimension.
|
||||
"""
|
||||
src = self.cname
|
||||
|
||||
code.putln("%(dst)s.data = %(src)s.data;" % locals())
|
||||
code.putln("%(dst)s.memview = %(src)s.memview;" % locals())
|
||||
code.put_incref_memoryviewslice(dst)
|
||||
|
||||
all_dimensions_direct = all(access == 'direct' for access, packing in self.type.axes)
|
||||
suboffset_dim_temp = []
|
||||
|
||||
def get_suboffset_dim():
|
||||
# create global temp variable at request
|
||||
if not suboffset_dim_temp:
|
||||
suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, manage_ref=False)
|
||||
code.putln("%s = -1;" % suboffset_dim)
|
||||
suboffset_dim_temp.append(suboffset_dim)
|
||||
return suboffset_dim_temp[0]
|
||||
|
||||
dim = -1
|
||||
new_ndim = 0
|
||||
for index in indices:
|
||||
if index.is_none:
|
||||
# newaxis
|
||||
for attrib, value in [('shape', 1), ('strides', 0), ('suboffsets', -1)]:
|
||||
code.putln("%s.%s[%d] = %d;" % (dst, attrib, new_ndim, value))
|
||||
|
||||
new_ndim += 1
|
||||
continue
|
||||
|
||||
dim += 1
|
||||
access, packing = self.type.axes[dim]
|
||||
|
||||
if isinstance(index, ExprNodes.SliceNode):
|
||||
# slice, unspecified dimension, or part of ellipsis
|
||||
d = dict(locals())
|
||||
for s in "start stop step".split():
|
||||
idx = getattr(index, s)
|
||||
have_idx = d['have_' + s] = not idx.is_none
|
||||
d[s] = idx.result() if have_idx else "0"
|
||||
|
||||
if not (d['have_start'] or d['have_stop'] or d['have_step']):
|
||||
# full slice (:), simply copy over the extent, stride
|
||||
# and suboffset. Also update suboffset_dim if needed
|
||||
d['access'] = access
|
||||
util_name = "SimpleSlice"
|
||||
else:
|
||||
util_name = "ToughSlice"
|
||||
d['error_goto'] = code.error_goto(index.pos)
|
||||
|
||||
new_ndim += 1
|
||||
else:
|
||||
# normal index
|
||||
idx = index.result()
|
||||
|
||||
indirect = access != 'direct'
|
||||
if indirect:
|
||||
generic = access == 'full'
|
||||
if new_ndim != 0:
|
||||
return error(index.pos,
|
||||
"All preceding dimensions must be "
|
||||
"indexed and not sliced")
|
||||
|
||||
d = dict(
|
||||
locals(),
|
||||
wraparound=int(directives['wraparound']),
|
||||
boundscheck=int(directives['boundscheck']),
|
||||
)
|
||||
if d['boundscheck']:
|
||||
d['error_goto'] = code.error_goto(index.pos)
|
||||
util_name = "SliceIndex"
|
||||
|
||||
_, impl = TempitaUtilityCode.load_as_string(util_name, "MemoryView_C.c", context=d)
|
||||
code.put(impl)
|
||||
|
||||
if suboffset_dim_temp:
|
||||
code.funcstate.release_temp(suboffset_dim_temp[0])
|
||||
|
||||
|
||||
def empty_slice(pos):
|
||||
none = ExprNodes.NoneNode(pos)
|
||||
return ExprNodes.SliceNode(pos, start=none,
|
||||
stop=none, step=none)
|
||||
|
||||
|
||||
def unellipsify(indices, ndim):
|
||||
result = []
|
||||
seen_ellipsis = False
|
||||
have_slices = False
|
||||
|
||||
newaxes = [newaxis for newaxis in indices if newaxis.is_none]
|
||||
n_indices = len(indices) - len(newaxes)
|
||||
|
||||
for index in indices:
|
||||
if isinstance(index, ExprNodes.EllipsisNode):
|
||||
have_slices = True
|
||||
full_slice = empty_slice(index.pos)
|
||||
|
||||
if seen_ellipsis:
|
||||
result.append(full_slice)
|
||||
else:
|
||||
nslices = ndim - n_indices + 1
|
||||
result.extend([full_slice] * nslices)
|
||||
seen_ellipsis = True
|
||||
else:
|
||||
have_slices = have_slices or index.is_slice or index.is_none
|
||||
result.append(index)
|
||||
|
||||
result_length = len(result) - len(newaxes)
|
||||
if result_length < ndim:
|
||||
have_slices = True
|
||||
nslices = ndim - result_length
|
||||
result.extend([empty_slice(indices[-1].pos)] * nslices)
|
||||
|
||||
return have_slices, result, newaxes
|
||||
|
||||
|
||||
def get_memoryview_flag(access, packing):
|
||||
if access == 'full' and packing in ('strided', 'follow'):
|
||||
return 'generic'
|
||||
elif access == 'full' and packing == 'contig':
|
||||
return 'generic_contiguous'
|
||||
elif access == 'ptr' and packing in ('strided', 'follow'):
|
||||
return 'indirect'
|
||||
elif access == 'ptr' and packing == 'contig':
|
||||
return 'indirect_contiguous'
|
||||
elif access == 'direct' and packing in ('strided', 'follow'):
|
||||
return 'strided'
|
||||
else:
|
||||
assert (access, packing) == ('direct', 'contig'), (access, packing)
|
||||
return 'contiguous'
|
||||
|
||||
|
||||
def get_is_contig_func_name(contig_type, ndim):
|
||||
assert contig_type in ('C', 'F')
|
||||
return "__pyx_memviewslice_is_contig_%s%d" % (contig_type, ndim)
|
||||
|
||||
|
||||
def get_is_contig_utility(contig_type, ndim):
|
||||
assert contig_type in ('C', 'F')
|
||||
C = dict(context, ndim=ndim, contig_type=contig_type)
|
||||
utility = load_memview_c_utility("MemviewSliceCheckContig", C, requires=[is_contig_utility])
|
||||
return utility
|
||||
|
||||
|
||||
def slice_iter(slice_type, slice_result, ndim, code):
|
||||
if slice_type.is_c_contig or slice_type.is_f_contig:
|
||||
return ContigSliceIter(slice_type, slice_result, ndim, code)
|
||||
else:
|
||||
return StridedSliceIter(slice_type, slice_result, ndim, code)
|
||||
|
||||
|
||||
class SliceIter(object):
|
||||
def __init__(self, slice_type, slice_result, ndim, code):
|
||||
self.slice_type = slice_type
|
||||
self.slice_result = slice_result
|
||||
self.code = code
|
||||
self.ndim = ndim
|
||||
|
||||
|
||||
class ContigSliceIter(SliceIter):
|
||||
def start_loops(self):
|
||||
code = self.code
|
||||
code.begin_block()
|
||||
|
||||
type_decl = self.slice_type.dtype.empty_declaration_code()
|
||||
|
||||
total_size = ' * '.join("%s.shape[%d]" % (self.slice_result, i)
|
||||
for i in range(self.ndim))
|
||||
code.putln("Py_ssize_t __pyx_temp_extent = %s;" % total_size)
|
||||
code.putln("Py_ssize_t __pyx_temp_idx;")
|
||||
code.putln("%s *__pyx_temp_pointer = (%s *) %s.data;" % (
|
||||
type_decl, type_decl, self.slice_result))
|
||||
code.putln("for (__pyx_temp_idx = 0; "
|
||||
"__pyx_temp_idx < __pyx_temp_extent; "
|
||||
"__pyx_temp_idx++) {")
|
||||
|
||||
return "__pyx_temp_pointer"
|
||||
|
||||
def end_loops(self):
|
||||
self.code.putln("__pyx_temp_pointer += 1;")
|
||||
self.code.putln("}")
|
||||
self.code.end_block()
|
||||
|
||||
|
||||
class StridedSliceIter(SliceIter):
|
||||
def start_loops(self):
|
||||
code = self.code
|
||||
code.begin_block()
|
||||
|
||||
for i in range(self.ndim):
|
||||
t = i, self.slice_result, i
|
||||
code.putln("Py_ssize_t __pyx_temp_extent_%d = %s.shape[%d];" % t)
|
||||
code.putln("Py_ssize_t __pyx_temp_stride_%d = %s.strides[%d];" % t)
|
||||
code.putln("char *__pyx_temp_pointer_%d;" % i)
|
||||
code.putln("Py_ssize_t __pyx_temp_idx_%d;" % i)
|
||||
|
||||
code.putln("__pyx_temp_pointer_0 = %s.data;" % self.slice_result)
|
||||
|
||||
for i in range(self.ndim):
|
||||
if i > 0:
|
||||
code.putln("__pyx_temp_pointer_%d = __pyx_temp_pointer_%d;" % (i, i - 1))
|
||||
|
||||
code.putln("for (__pyx_temp_idx_%d = 0; "
|
||||
"__pyx_temp_idx_%d < __pyx_temp_extent_%d; "
|
||||
"__pyx_temp_idx_%d++) {" % (i, i, i, i))
|
||||
|
||||
return "__pyx_temp_pointer_%d" % (self.ndim - 1)
|
||||
|
||||
def end_loops(self):
|
||||
code = self.code
|
||||
for i in range(self.ndim - 1, -1, -1):
|
||||
code.putln("__pyx_temp_pointer_%d += __pyx_temp_stride_%d;" % (i, i))
|
||||
code.putln("}")
|
||||
|
||||
code.end_block()
|
||||
|
||||
|
||||
def copy_c_or_fortran_cname(memview):
|
||||
if memview.is_c_contig:
|
||||
c_or_f = 'c'
|
||||
else:
|
||||
c_or_f = 'f'
|
||||
|
||||
return "__pyx_memoryview_copy_slice_%s_%s" % (
|
||||
memview.specialization_suffix(), c_or_f)
|
||||
|
||||
|
||||
def get_copy_new_utility(pos, from_memview, to_memview):
|
||||
if (from_memview.dtype != to_memview.dtype and
|
||||
not (from_memview.dtype.is_const and from_memview.dtype.const_base_type == to_memview.dtype)):
|
||||
error(pos, "dtypes must be the same!")
|
||||
return
|
||||
if len(from_memview.axes) != len(to_memview.axes):
|
||||
error(pos, "number of dimensions must be same")
|
||||
return
|
||||
if not (to_memview.is_c_contig or to_memview.is_f_contig):
|
||||
error(pos, "to_memview must be c or f contiguous.")
|
||||
return
|
||||
|
||||
for (access, packing) in from_memview.axes:
|
||||
if access != 'direct':
|
||||
error(pos, "cannot handle 'full' or 'ptr' access at this time.")
|
||||
return
|
||||
|
||||
if to_memview.is_c_contig:
|
||||
mode = 'c'
|
||||
contig_flag = memview_c_contiguous
|
||||
elif to_memview.is_f_contig:
|
||||
mode = 'fortran'
|
||||
contig_flag = memview_f_contiguous
|
||||
|
||||
return load_memview_c_utility(
|
||||
"CopyContentsUtility",
|
||||
context=dict(
|
||||
context,
|
||||
mode=mode,
|
||||
dtype_decl=to_memview.dtype.empty_declaration_code(),
|
||||
contig_flag=contig_flag,
|
||||
ndim=to_memview.ndim,
|
||||
func_cname=copy_c_or_fortran_cname(to_memview),
|
||||
dtype_is_object=int(to_memview.dtype.is_pyobject)),
|
||||
requires=[copy_contents_new_utility])
|
||||
|
||||
|
||||
def get_axes_specs(env, axes):
|
||||
'''
|
||||
get_axes_specs(env, axes) -> list of (access, packing) specs for each axis.
|
||||
access is one of 'full', 'ptr' or 'direct'
|
||||
packing is one of 'contig', 'strided' or 'follow'
|
||||
'''
|
||||
|
||||
cythonscope = env.global_scope().context.cython_scope
|
||||
cythonscope.load_cythonscope()
|
||||
viewscope = cythonscope.viewscope
|
||||
|
||||
access_specs = tuple([viewscope.lookup(name)
|
||||
for name in ('full', 'direct', 'ptr')])
|
||||
packing_specs = tuple([viewscope.lookup(name)
|
||||
for name in ('contig', 'strided', 'follow')])
|
||||
|
||||
is_f_contig, is_c_contig = False, False
|
||||
default_access, default_packing = 'direct', 'strided'
|
||||
cf_access, cf_packing = default_access, 'follow'
|
||||
|
||||
axes_specs = []
|
||||
# analyse all axes.
|
||||
for idx, axis in enumerate(axes):
|
||||
if not axis.start.is_none:
|
||||
raise CompileError(axis.start.pos, START_ERR)
|
||||
|
||||
if not axis.stop.is_none:
|
||||
raise CompileError(axis.stop.pos, STOP_ERR)
|
||||
|
||||
if axis.step.is_none:
|
||||
axes_specs.append((default_access, default_packing))
|
||||
|
||||
elif isinstance(axis.step, IntNode):
|
||||
# the packing for the ::1 axis is contiguous,
|
||||
# all others are cf_packing.
|
||||
if axis.step.compile_time_value(env) != 1:
|
||||
raise CompileError(axis.step.pos, STEP_ERR)
|
||||
|
||||
axes_specs.append((cf_access, 'cfcontig'))
|
||||
|
||||
elif isinstance(axis.step, (NameNode, AttributeNode)):
|
||||
entry = _get_resolved_spec(env, axis.step)
|
||||
if entry.name in view_constant_to_access_packing:
|
||||
axes_specs.append(view_constant_to_access_packing[entry.name])
|
||||
else:
|
||||
raise CompileError(axis.step.pos, INVALID_ERR)
|
||||
|
||||
else:
|
||||
raise CompileError(axis.step.pos, INVALID_ERR)
|
||||
|
||||
# First, find out if we have a ::1 somewhere
|
||||
contig_dim = 0
|
||||
is_contig = False
|
||||
for idx, (access, packing) in enumerate(axes_specs):
|
||||
if packing == 'cfcontig':
|
||||
if is_contig:
|
||||
raise CompileError(axis.step.pos, BOTH_CF_ERR)
|
||||
|
||||
contig_dim = idx
|
||||
axes_specs[idx] = (access, 'contig')
|
||||
is_contig = True
|
||||
|
||||
if is_contig:
|
||||
# We have a ::1 somewhere, see if we're C or Fortran contiguous
|
||||
if contig_dim == len(axes) - 1:
|
||||
is_c_contig = True
|
||||
else:
|
||||
is_f_contig = True
|
||||
|
||||
if contig_dim and not axes_specs[contig_dim - 1][0] in ('full', 'ptr'):
|
||||
raise CompileError(axes[contig_dim].pos,
|
||||
"Fortran contiguous specifier must follow an indirect dimension")
|
||||
|
||||
if is_c_contig:
|
||||
# Contiguous in the last dimension, find the last indirect dimension
|
||||
contig_dim = -1
|
||||
for idx, (access, packing) in enumerate(reversed(axes_specs)):
|
||||
if access in ('ptr', 'full'):
|
||||
contig_dim = len(axes) - idx - 1
|
||||
|
||||
# Replace 'strided' with 'follow' for any dimension following the last
|
||||
# indirect dimension, the first dimension or the dimension following
|
||||
# the ::1.
|
||||
# int[::indirect, ::1, :, :]
|
||||
# ^ ^
|
||||
# int[::indirect, :, :, ::1]
|
||||
# ^ ^
|
||||
start = contig_dim + 1
|
||||
stop = len(axes) - is_c_contig
|
||||
for idx, (access, packing) in enumerate(axes_specs[start:stop]):
|
||||
idx = contig_dim + 1 + idx
|
||||
if access != 'direct':
|
||||
raise CompileError(axes[idx].pos,
|
||||
"Indirect dimension may not follow "
|
||||
"Fortran contiguous dimension")
|
||||
if packing == 'contig':
|
||||
raise CompileError(axes[idx].pos,
|
||||
"Dimension may not be contiguous")
|
||||
axes_specs[idx] = (access, cf_packing)
|
||||
|
||||
if is_c_contig:
|
||||
# For C contiguity, we need to fix the 'contig' dimension
|
||||
# after the loop
|
||||
a, p = axes_specs[-1]
|
||||
axes_specs[-1] = a, 'contig'
|
||||
|
||||
validate_axes_specs([axis.start.pos for axis in axes],
|
||||
axes_specs,
|
||||
is_c_contig,
|
||||
is_f_contig)
|
||||
|
||||
return axes_specs
|
||||
|
||||
|
||||
def validate_axes(pos, axes):
|
||||
if len(axes) >= Options.buffer_max_dims:
|
||||
error(pos, "More dimensions than the maximum number"
|
||||
" of buffer dimensions were used.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def is_cf_contig(specs):
|
||||
is_c_contig = is_f_contig = False
|
||||
|
||||
if len(specs) == 1 and specs == [('direct', 'contig')]:
|
||||
is_c_contig = True
|
||||
|
||||
elif (specs[-1] == ('direct','contig') and
|
||||
all(axis == ('direct','follow') for axis in specs[:-1])):
|
||||
# c_contiguous: 'follow', 'follow', ..., 'follow', 'contig'
|
||||
is_c_contig = True
|
||||
|
||||
elif (len(specs) > 1 and
|
||||
specs[0] == ('direct','contig') and
|
||||
all(axis == ('direct','follow') for axis in specs[1:])):
|
||||
# f_contiguous: 'contig', 'follow', 'follow', ..., 'follow'
|
||||
is_f_contig = True
|
||||
|
||||
return is_c_contig, is_f_contig
|
||||
|
||||
|
||||
def get_mode(specs):
|
||||
is_c_contig, is_f_contig = is_cf_contig(specs)
|
||||
|
||||
if is_c_contig:
|
||||
return 'c'
|
||||
elif is_f_contig:
|
||||
return 'fortran'
|
||||
|
||||
for access, packing in specs:
|
||||
if access in ('ptr', 'full'):
|
||||
return 'full'
|
||||
|
||||
return 'strided'
|
||||
|
||||
view_constant_to_access_packing = {
|
||||
'generic': ('full', 'strided'),
|
||||
'strided': ('direct', 'strided'),
|
||||
'indirect': ('ptr', 'strided'),
|
||||
'generic_contiguous': ('full', 'contig'),
|
||||
'contiguous': ('direct', 'contig'),
|
||||
'indirect_contiguous': ('ptr', 'contig'),
|
||||
}
|
||||
|
||||
def validate_axes_specs(positions, specs, is_c_contig, is_f_contig):
|
||||
|
||||
packing_specs = ('contig', 'strided', 'follow')
|
||||
access_specs = ('direct', 'ptr', 'full')
|
||||
|
||||
# is_c_contig, is_f_contig = is_cf_contig(specs)
|
||||
|
||||
has_contig = has_follow = has_strided = has_generic_contig = False
|
||||
|
||||
last_indirect_dimension = -1
|
||||
for idx, (access, packing) in enumerate(specs):
|
||||
if access == 'ptr':
|
||||
last_indirect_dimension = idx
|
||||
|
||||
for idx, (pos, (access, packing)) in enumerate(zip(positions, specs)):
|
||||
|
||||
if not (access in access_specs and
|
||||
packing in packing_specs):
|
||||
raise CompileError(pos, "Invalid axes specification.")
|
||||
|
||||
if packing == 'strided':
|
||||
has_strided = True
|
||||
elif packing == 'contig':
|
||||
if has_contig:
|
||||
raise CompileError(pos, "Only one direct contiguous "
|
||||
"axis may be specified.")
|
||||
|
||||
valid_contig_dims = last_indirect_dimension + 1, len(specs) - 1
|
||||
if idx not in valid_contig_dims and access != 'ptr':
|
||||
if last_indirect_dimension + 1 != len(specs) - 1:
|
||||
dims = "dimensions %d and %d" % valid_contig_dims
|
||||
else:
|
||||
dims = "dimension %d" % valid_contig_dims[0]
|
||||
|
||||
raise CompileError(pos, "Only %s may be contiguous and direct" % dims)
|
||||
|
||||
has_contig = access != 'ptr'
|
||||
elif packing == 'follow':
|
||||
if has_strided:
|
||||
raise CompileError(pos, "A memoryview cannot have both follow and strided axis specifiers.")
|
||||
if not (is_c_contig or is_f_contig):
|
||||
raise CompileError(pos, "Invalid use of the follow specifier.")
|
||||
|
||||
if access in ('ptr', 'full'):
|
||||
has_strided = False
|
||||
|
||||
def _get_resolved_spec(env, spec):
|
||||
# spec must be a NameNode or an AttributeNode
|
||||
if isinstance(spec, NameNode):
|
||||
return _resolve_NameNode(env, spec)
|
||||
elif isinstance(spec, AttributeNode):
|
||||
return _resolve_AttributeNode(env, spec)
|
||||
else:
|
||||
raise CompileError(spec.pos, INVALID_ERR)
|
||||
|
||||
def _resolve_NameNode(env, node):
|
||||
try:
|
||||
resolved_name = env.lookup(node.name).name
|
||||
except AttributeError:
|
||||
raise CompileError(node.pos, INVALID_ERR)
|
||||
|
||||
viewscope = env.global_scope().context.cython_scope.viewscope
|
||||
entry = viewscope.lookup(resolved_name)
|
||||
if entry is None:
|
||||
raise CompileError(node.pos, NOT_CIMPORTED_ERR)
|
||||
|
||||
return entry
|
||||
|
||||
def _resolve_AttributeNode(env, node):
|
||||
path = []
|
||||
while isinstance(node, AttributeNode):
|
||||
path.insert(0, node.attribute)
|
||||
node = node.obj
|
||||
if isinstance(node, NameNode):
|
||||
path.insert(0, node.name)
|
||||
else:
|
||||
raise CompileError(node.pos, EXPR_ERR)
|
||||
modnames = path[:-1]
|
||||
# must be at least 1 module name, o/w not an AttributeNode.
|
||||
assert modnames
|
||||
|
||||
scope = env
|
||||
for modname in modnames:
|
||||
mod = scope.lookup(modname)
|
||||
if not mod or not mod.as_module:
|
||||
raise CompileError(
|
||||
node.pos, "undeclared name not builtin: %s" % modname)
|
||||
scope = mod.as_module
|
||||
|
||||
entry = scope.lookup(path[-1])
|
||||
if not entry:
|
||||
raise CompileError(node.pos, "No such attribute '%s'" % path[-1])
|
||||
|
||||
return entry
|
||||
|
||||
#
|
||||
### Utility loading
|
||||
#
|
||||
|
||||
def load_memview_cy_utility(util_code_name, context=None, **kwargs):
|
||||
return CythonUtilityCode.load(util_code_name, "MemoryView.pyx",
|
||||
context=context, **kwargs)
|
||||
|
||||
def load_memview_c_utility(util_code_name, context=None, **kwargs):
|
||||
if context is None:
|
||||
return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs)
|
||||
else:
|
||||
return TempitaUtilityCode.load(util_code_name, "MemoryView_C.c",
|
||||
context=context, **kwargs)
|
||||
|
||||
def use_cython_array_utility_code(env):
|
||||
cython_scope = env.global_scope().context.cython_scope
|
||||
cython_scope.load_cythonscope()
|
||||
cython_scope.viewscope.lookup('array_cwrapper').used = True
|
||||
|
||||
context = {
|
||||
'memview_struct_name': memview_objstruct_cname,
|
||||
'max_dims': Options.buffer_max_dims,
|
||||
'memviewslice_name': memviewslice_cname,
|
||||
'memslice_init': memslice_entry_init,
|
||||
}
|
||||
memviewslice_declare_code = load_memview_c_utility(
|
||||
"MemviewSliceStruct",
|
||||
context=context,
|
||||
requires=[])
|
||||
|
||||
atomic_utility = load_memview_c_utility("Atomics", context)
|
||||
|
||||
memviewslice_init_code = load_memview_c_utility(
|
||||
"MemviewSliceInit",
|
||||
context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims),
|
||||
requires=[memviewslice_declare_code,
|
||||
atomic_utility],
|
||||
)
|
||||
|
||||
memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex")
|
||||
|
||||
typeinfo_to_format_code = load_memview_cy_utility(
|
||||
"BufferFormatFromTypeInfo", requires=[Buffer._typeinfo_to_format_code])
|
||||
|
||||
is_contig_utility = load_memview_c_utility("MemviewSliceIsContig", context)
|
||||
overlapping_utility = load_memview_c_utility("OverlappingSlices", context)
|
||||
copy_contents_new_utility = load_memview_c_utility(
|
||||
"MemviewSliceCopyTemplate",
|
||||
context,
|
||||
requires=[], # require cython_array_utility_code
|
||||
)
|
||||
|
||||
view_utility_code = load_memview_cy_utility(
|
||||
"View.MemoryView",
|
||||
context=context,
|
||||
requires=[Buffer.GetAndReleaseBufferUtilityCode(),
|
||||
Buffer.buffer_struct_declare_code,
|
||||
Buffer.buffer_formats_declare_code,
|
||||
memviewslice_init_code,
|
||||
is_contig_utility,
|
||||
overlapping_utility,
|
||||
copy_contents_new_utility,
|
||||
ModuleNode.capsule_utility_code],
|
||||
)
|
||||
view_utility_whitelist = ('array', 'memoryview', 'array_cwrapper',
|
||||
'generic', 'strided', 'indirect', 'contiguous',
|
||||
'indirect_contiguous')
|
||||
|
||||
memviewslice_declare_code.requires.append(view_utility_code)
|
||||
copy_contents_new_utility.requires.append(view_utility_code)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
#
|
||||
# C naming conventions
|
||||
#
|
||||
#
|
||||
# Prefixes for generating C names.
|
||||
# Collected here to facilitate ensuring uniqueness.
|
||||
#
|
||||
|
||||
pyrex_prefix = "__pyx_"
|
||||
|
||||
|
||||
codewriter_temp_prefix = pyrex_prefix + "t_"
|
||||
|
||||
temp_prefix = u"__cyt_"
|
||||
|
||||
builtin_prefix = pyrex_prefix + "builtin_"
|
||||
arg_prefix = pyrex_prefix + "arg_"
|
||||
funcdoc_prefix = pyrex_prefix + "doc_"
|
||||
enum_prefix = pyrex_prefix + "e_"
|
||||
func_prefix = pyrex_prefix + "f_"
|
||||
func_prefix_api = pyrex_prefix + "api_f_"
|
||||
pyfunc_prefix = pyrex_prefix + "pf_"
|
||||
pywrap_prefix = pyrex_prefix + "pw_"
|
||||
genbody_prefix = pyrex_prefix + "gb_"
|
||||
gstab_prefix = pyrex_prefix + "getsets_"
|
||||
prop_get_prefix = pyrex_prefix + "getprop_"
|
||||
const_prefix = pyrex_prefix + "k_"
|
||||
py_const_prefix = pyrex_prefix + "kp_"
|
||||
label_prefix = pyrex_prefix + "L"
|
||||
pymethdef_prefix = pyrex_prefix + "mdef_"
|
||||
method_wrapper_prefix = pyrex_prefix + "specialmethod_"
|
||||
methtab_prefix = pyrex_prefix + "methods_"
|
||||
memtab_prefix = pyrex_prefix + "members_"
|
||||
objstruct_prefix = pyrex_prefix + "obj_"
|
||||
typeptr_prefix = pyrex_prefix + "ptype_"
|
||||
prop_set_prefix = pyrex_prefix + "setprop_"
|
||||
type_prefix = pyrex_prefix + "t_"
|
||||
typeobj_prefix = pyrex_prefix + "type_"
|
||||
var_prefix = pyrex_prefix + "v_"
|
||||
varptr_prefix = pyrex_prefix + "vp_"
|
||||
varptr_prefix_api = pyrex_prefix + "api_vp_"
|
||||
wrapperbase_prefix= pyrex_prefix + "wrapperbase_"
|
||||
pybuffernd_prefix = pyrex_prefix + "pybuffernd_"
|
||||
pybufferstruct_prefix = pyrex_prefix + "pybuffer_"
|
||||
vtable_prefix = pyrex_prefix + "vtable_"
|
||||
vtabptr_prefix = pyrex_prefix + "vtabptr_"
|
||||
vtabstruct_prefix = pyrex_prefix + "vtabstruct_"
|
||||
opt_arg_prefix = pyrex_prefix + "opt_args_"
|
||||
convert_func_prefix = pyrex_prefix + "convert_"
|
||||
closure_scope_prefix = pyrex_prefix + "scope_"
|
||||
closure_class_prefix = pyrex_prefix + "scope_struct_"
|
||||
lambda_func_prefix = pyrex_prefix + "lambda_"
|
||||
module_is_main = pyrex_prefix + "module_is_main_"
|
||||
defaults_struct_prefix = pyrex_prefix + "defaults"
|
||||
dynamic_args_cname = pyrex_prefix + "dynamic_args"
|
||||
|
||||
interned_prefixes = {
|
||||
'str': pyrex_prefix + "n_",
|
||||
'int': pyrex_prefix + "int_",
|
||||
'float': pyrex_prefix + "float_",
|
||||
'tuple': pyrex_prefix + "tuple_",
|
||||
'codeobj': pyrex_prefix + "codeobj_",
|
||||
'slice': pyrex_prefix + "slice_",
|
||||
'ustring': pyrex_prefix + "ustring_",
|
||||
'umethod': pyrex_prefix + "umethod_",
|
||||
}
|
||||
|
||||
ctuple_type_prefix = pyrex_prefix + "ctuple_"
|
||||
args_cname = pyrex_prefix + "args"
|
||||
generator_cname = pyrex_prefix + "generator"
|
||||
sent_value_cname = pyrex_prefix + "sent_value"
|
||||
pykwdlist_cname = pyrex_prefix + "pyargnames"
|
||||
obj_base_cname = pyrex_prefix + "base"
|
||||
builtins_cname = pyrex_prefix + "b"
|
||||
preimport_cname = pyrex_prefix + "i"
|
||||
moddict_cname = pyrex_prefix + "d"
|
||||
dummy_cname = pyrex_prefix + "dummy"
|
||||
filename_cname = pyrex_prefix + "filename"
|
||||
modulename_cname = pyrex_prefix + "modulename"
|
||||
filetable_cname = pyrex_prefix + "f"
|
||||
intern_tab_cname = pyrex_prefix + "intern_tab"
|
||||
kwds_cname = pyrex_prefix + "kwds"
|
||||
lineno_cname = pyrex_prefix + "lineno"
|
||||
clineno_cname = pyrex_prefix + "clineno"
|
||||
cfilenm_cname = pyrex_prefix + "cfilenm"
|
||||
local_tstate_cname = pyrex_prefix + "tstate"
|
||||
module_cname = pyrex_prefix + "m"
|
||||
moddoc_cname = pyrex_prefix + "mdoc"
|
||||
methtable_cname = pyrex_prefix + "methods"
|
||||
retval_cname = pyrex_prefix + "r"
|
||||
reqd_kwds_cname = pyrex_prefix + "reqd_kwds"
|
||||
self_cname = pyrex_prefix + "self"
|
||||
stringtab_cname = pyrex_prefix + "string_tab"
|
||||
vtabslot_cname = pyrex_prefix + "vtab"
|
||||
c_api_tab_cname = pyrex_prefix + "c_api_tab"
|
||||
gilstate_cname = pyrex_prefix + "state"
|
||||
skip_dispatch_cname = pyrex_prefix + "skip_dispatch"
|
||||
empty_tuple = pyrex_prefix + "empty_tuple"
|
||||
empty_bytes = pyrex_prefix + "empty_bytes"
|
||||
empty_unicode = pyrex_prefix + "empty_unicode"
|
||||
print_function = pyrex_prefix + "print"
|
||||
print_function_kwargs = pyrex_prefix + "print_kwargs"
|
||||
cleanup_cname = pyrex_prefix + "module_cleanup"
|
||||
pymoduledef_cname = pyrex_prefix + "moduledef"
|
||||
pymoduledef_slots_cname = pyrex_prefix + "moduledef_slots"
|
||||
pymodinit_module_arg = pyrex_prefix + "pyinit_module"
|
||||
pymodule_create_func_cname = pyrex_prefix + "pymod_create"
|
||||
pymodule_exec_func_cname = pyrex_prefix + "pymod_exec"
|
||||
optional_args_cname = pyrex_prefix + "optional_args"
|
||||
import_star = pyrex_prefix + "import_star"
|
||||
import_star_set = pyrex_prefix + "import_star_set"
|
||||
outer_scope_cname= pyrex_prefix + "outer_scope"
|
||||
cur_scope_cname = pyrex_prefix + "cur_scope"
|
||||
enc_scope_cname = pyrex_prefix + "enc_scope"
|
||||
frame_cname = pyrex_prefix + "frame"
|
||||
frame_code_cname = pyrex_prefix + "frame_code"
|
||||
binding_cfunc = pyrex_prefix + "binding_PyCFunctionType"
|
||||
fused_func_prefix = pyrex_prefix + 'fuse_'
|
||||
quick_temp_cname = pyrex_prefix + "temp" # temp variable for quick'n'dirty temping
|
||||
tp_dict_version_temp = pyrex_prefix + "tp_dict_version"
|
||||
obj_dict_version_temp = pyrex_prefix + "obj_dict_version"
|
||||
type_dict_guard_temp = pyrex_prefix + "type_dict_guard"
|
||||
cython_runtime_cname = pyrex_prefix + "cython_runtime"
|
||||
|
||||
global_code_object_cache_find = pyrex_prefix + 'find_code_object'
|
||||
global_code_object_cache_insert = pyrex_prefix + 'insert_code_object'
|
||||
|
||||
genexpr_id_ref = 'genexpr'
|
||||
freelist_name = 'freelist'
|
||||
freecount_name = 'freecount'
|
||||
|
||||
line_c_macro = "__LINE__"
|
||||
|
||||
file_c_macro = "__FILE__"
|
||||
|
||||
extern_c_macro = pyrex_prefix.upper() + "EXTERN_C"
|
||||
|
||||
exc_type_name = pyrex_prefix + "exc_type"
|
||||
exc_value_name = pyrex_prefix + "exc_value"
|
||||
exc_tb_name = pyrex_prefix + "exc_tb"
|
||||
exc_lineno_name = pyrex_prefix + "exc_lineno"
|
||||
|
||||
parallel_exc_type = pyrex_prefix + "parallel_exc_type"
|
||||
parallel_exc_value = pyrex_prefix + "parallel_exc_value"
|
||||
parallel_exc_tb = pyrex_prefix + "parallel_exc_tb"
|
||||
parallel_filename = pyrex_prefix + "parallel_filename"
|
||||
parallel_lineno = pyrex_prefix + "parallel_lineno"
|
||||
parallel_clineno = pyrex_prefix + "parallel_clineno"
|
||||
parallel_why = pyrex_prefix + "parallel_why"
|
||||
|
||||
exc_vars = (exc_type_name, exc_value_name, exc_tb_name)
|
||||
|
||||
api_name = pyrex_prefix + "capi__"
|
||||
|
||||
h_guard_prefix = "__PYX_HAVE__"
|
||||
api_guard_prefix = "__PYX_HAVE_API__"
|
||||
api_func_guard = "__PYX_HAVE_API_FUNC_"
|
||||
|
||||
PYX_NAN = "__PYX_NAN()"
|
||||
|
||||
def py_version_hex(major, minor=0, micro=0, release_level=0, release_serial=0):
|
||||
return (major << 24) | (minor << 16) | (micro << 8) | (release_level << 4) | (release_serial)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,550 @@
|
||||
#
|
||||
# Cython - Compilation-wide options and pragma declarations
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
||||
class ShouldBeFromDirective(object):
|
||||
|
||||
known_directives = []
|
||||
|
||||
def __init__(self, options_name, directive_name=None, disallow=False):
|
||||
self.options_name = options_name
|
||||
self.directive_name = directive_name or options_name
|
||||
self.disallow = disallow
|
||||
self.known_directives.append(self)
|
||||
|
||||
def __nonzero__(self):
|
||||
self._bad_access()
|
||||
|
||||
def __int__(self):
|
||||
self._bad_access()
|
||||
|
||||
def _bad_access(self):
|
||||
raise RuntimeError(repr(self))
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"Illegal access of '%s' from Options module rather than directive '%s'"
|
||||
% (self.options_name, self.directive_name))
|
||||
|
||||
|
||||
"""
|
||||
The members of this module are documented using autodata in
|
||||
Cython/docs/src/reference/compilation.rst.
|
||||
See http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-autoattribute
|
||||
for how autodata works.
|
||||
Descriptions of those members should start with a #:
|
||||
Donc forget to keep the docs in sync by removing and adding
|
||||
the members in both this file and the .rst file.
|
||||
"""
|
||||
|
||||
#: Whether or not to include docstring in the Python extension. If False, the binary size
|
||||
#: will be smaller, but the ``__doc__`` attribute of any class or function will be an
|
||||
#: empty string.
|
||||
docstrings = True
|
||||
|
||||
#: Embed the source code position in the docstrings of functions and classes.
|
||||
embed_pos_in_docstring = False
|
||||
|
||||
#: Copy the original source code line by line into C code comments
|
||||
#: in the generated code file to help with understanding the output.
|
||||
#: This is also required for coverage analysis.
|
||||
emit_code_comments = True
|
||||
|
||||
# undocumented
|
||||
pre_import = None
|
||||
|
||||
#: Decref global variables in each module on exit for garbage collection.
|
||||
#: 0: None, 1+: interned objects, 2+: cdef globals, 3+: types objects
|
||||
#: Mostly for reducing noise in Valgrind as it typically executes at process exit
|
||||
#: (when all memory will be reclaimed anyways).
|
||||
#: Note that directly or indirectly executed cleanup code that makes use of global
|
||||
#: variables or types may no longer be safe when enabling the respective level since
|
||||
#: there is no guaranteed order in which the (reference counted) objects will
|
||||
#: be cleaned up. The order can change due to live references and reference cycles.
|
||||
generate_cleanup_code = False
|
||||
|
||||
#: Should tp_clear() set object fields to None instead of clearing them to NULL?
|
||||
clear_to_none = True
|
||||
|
||||
#: Generate an annotated HTML version of the input source files for debugging and optimisation purposes.
|
||||
#: This has the same effect as the ``annotate`` argument in :func:`cythonize`.
|
||||
annotate = False
|
||||
|
||||
# When annotating source files in HTML, include coverage information from
|
||||
# this file.
|
||||
annotate_coverage_xml = None
|
||||
|
||||
#: This will abort the compilation on the first error occurred rather than trying
|
||||
#: to keep going and printing further error messages.
|
||||
fast_fail = False
|
||||
|
||||
#: Turn all warnings into errors.
|
||||
warning_errors = False
|
||||
|
||||
#: Make unknown names an error. Python raises a NameError when
|
||||
#: encountering unknown names at runtime, whereas this option makes
|
||||
#: them a compile time error. If you want full Python compatibility,
|
||||
#: you should disable this option and also 'cache_builtins'.
|
||||
error_on_unknown_names = True
|
||||
|
||||
#: Make uninitialized local variable reference a compile time error.
|
||||
#: Python raises UnboundLocalError at runtime, whereas this option makes
|
||||
#: them a compile time error. Note that this option affects only variables
|
||||
#: of "python object" type.
|
||||
error_on_uninitialized = True
|
||||
|
||||
#: This will convert statements of the form ``for i in range(...)``
|
||||
#: to ``for i from ...`` when ``i`` is a C integer type, and the direction
|
||||
#: (i.e. sign of step) can be determined.
|
||||
#: WARNING: This may change the semantics if the range causes assignment to
|
||||
#: i to overflow. Specifically, if this option is set, an error will be
|
||||
#: raised before the loop is entered, whereas without this option the loop
|
||||
#: will execute until an overflowing value is encountered.
|
||||
convert_range = True
|
||||
|
||||
#: Perform lookups on builtin names only once, at module initialisation
|
||||
#: time. This will prevent the module from getting imported if a
|
||||
#: builtin name that it uses cannot be found during initialisation.
|
||||
#: Default is True.
|
||||
#: Note that some legacy builtins are automatically remapped
|
||||
#: from their Python 2 names to their Python 3 names by Cython
|
||||
#: when building in Python 3.x,
|
||||
#: so that they do not get in the way even if this option is enabled.
|
||||
cache_builtins = True
|
||||
|
||||
#: Generate branch prediction hints to speed up error handling etc.
|
||||
gcc_branch_hints = True
|
||||
|
||||
#: Enable this to allow one to write ``your_module.foo = ...`` to overwrite the
|
||||
#: definition if the cpdef function foo, at the cost of an extra dictionary
|
||||
#: lookup on every call.
|
||||
#: If this is false it generates only the Python wrapper and no override check.
|
||||
lookup_module_cpdef = False
|
||||
|
||||
#: Whether or not to embed the Python interpreter, for use in making a
|
||||
#: standalone executable or calling from external libraries.
|
||||
#: This will provide a C function which initialises the interpreter and
|
||||
#: executes the body of this module.
|
||||
#: See `this demo <https://github.com/cython/cython/tree/master/Demos/embed>`_
|
||||
#: for a concrete example.
|
||||
#: If true, the initialisation function is the C main() function, but
|
||||
#: this option can also be set to a non-empty string to provide a function name explicitly.
|
||||
#: Default is False.
|
||||
embed = None
|
||||
|
||||
# In previous iterations of Cython, globals() gave the first non-Cython module
|
||||
# globals in the call stack. Sage relies on this behavior for variable injection.
|
||||
old_style_globals = ShouldBeFromDirective('old_style_globals')
|
||||
|
||||
#: Allows cimporting from a pyx file without a pxd file.
|
||||
cimport_from_pyx = False
|
||||
|
||||
#: Maximum number of dimensions for buffers -- set lower than number of
|
||||
#: dimensions in numpy, as
|
||||
#: slices are passed by value and involve a lot of copying.
|
||||
buffer_max_dims = 8
|
||||
|
||||
#: Number of function closure instances to keep in a freelist (0: no freelists)
|
||||
closure_freelist_size = 8
|
||||
|
||||
|
||||
def get_directive_defaults():
|
||||
# To add an item to this list, all accesses should be changed to use the new
|
||||
# directive, and the global option itself should be set to an instance of
|
||||
# ShouldBeFromDirective.
|
||||
for old_option in ShouldBeFromDirective.known_directives:
|
||||
value = globals().get(old_option.options_name)
|
||||
assert old_option.directive_name in _directive_defaults
|
||||
if not isinstance(value, ShouldBeFromDirective):
|
||||
if old_option.disallow:
|
||||
raise RuntimeError(
|
||||
"Option '%s' must be set from directive '%s'" % (
|
||||
old_option.option_name, old_option.directive_name))
|
||||
else:
|
||||
# Warn?
|
||||
_directive_defaults[old_option.directive_name] = value
|
||||
return _directive_defaults
|
||||
|
||||
# Declare compiler directives
|
||||
_directive_defaults = {
|
||||
'boundscheck' : True,
|
||||
'nonecheck' : False,
|
||||
'initializedcheck' : True,
|
||||
'embedsignature' : False,
|
||||
'auto_cpdef': False,
|
||||
'auto_pickle': None,
|
||||
'cdivision': False, # was True before 0.12
|
||||
'cdivision_warnings': False,
|
||||
'c_api_binop_methods': True,
|
||||
'overflowcheck': False,
|
||||
'overflowcheck.fold': True,
|
||||
'always_allow_keywords': False,
|
||||
'allow_none_for_extension_args': True,
|
||||
'wraparound' : True,
|
||||
'ccomplex' : False, # use C99/C++ for complex types and arith
|
||||
'callspec' : "",
|
||||
'nogil' : False,
|
||||
'profile': False,
|
||||
'linetrace': False,
|
||||
'emit_code_comments': True, # copy original source code into C code comments
|
||||
'annotation_typing': True, # read type declarations from Python function annotations
|
||||
'infer_types': None,
|
||||
'infer_types.verbose': False,
|
||||
'autotestdict': True,
|
||||
'autotestdict.cdef': False,
|
||||
'autotestdict.all': False,
|
||||
'language_level': None,
|
||||
'fast_getattr': False, # Undocumented until we come up with a better way to handle this everywhere.
|
||||
'py2_import': False, # For backward compatibility of Cython's source code in Py3 source mode
|
||||
'preliminary_late_includes_cy28': False, # Temporary directive in 0.28, to be removed in a later version (see GH#2079).
|
||||
'iterable_coroutine': False, # Make async coroutines backwards compatible with the old asyncio yield-from syntax.
|
||||
'c_string_type': 'bytes',
|
||||
'c_string_encoding': '',
|
||||
'type_version_tag': True, # enables Py_TPFLAGS_HAVE_VERSION_TAG on extension types
|
||||
'unraisable_tracebacks': True,
|
||||
'old_style_globals': False,
|
||||
'np_pythran': False,
|
||||
'fast_gil': False,
|
||||
|
||||
# set __file__ and/or __path__ to known source/target path at import time (instead of not having them available)
|
||||
'set_initial_path' : None, # SOURCEFILE or "/full/path/to/module"
|
||||
|
||||
'warn': None,
|
||||
'warn.undeclared': False,
|
||||
'warn.unreachable': True,
|
||||
'warn.maybe_uninitialized': False,
|
||||
'warn.unused': False,
|
||||
'warn.unused_arg': False,
|
||||
'warn.unused_result': False,
|
||||
'warn.multiple_declarators': True,
|
||||
|
||||
# optimizations
|
||||
'optimize.inline_defnode_calls': True,
|
||||
'optimize.unpack_method_calls': True, # increases code size when True
|
||||
'optimize.unpack_method_calls_in_pyinit': False, # uselessly increases code size when True
|
||||
'optimize.use_switch': True,
|
||||
|
||||
# remove unreachable code
|
||||
'remove_unreachable': True,
|
||||
|
||||
# control flow debug directives
|
||||
'control_flow.dot_output': "", # Graphviz output filename
|
||||
'control_flow.dot_annotate_defs': False, # Annotate definitions
|
||||
|
||||
# test support
|
||||
'test_assert_path_exists' : [],
|
||||
'test_fail_if_path_exists' : [],
|
||||
|
||||
# experimental, subject to change
|
||||
'binding': None,
|
||||
|
||||
'formal_grammar': False,
|
||||
}
|
||||
|
||||
# Extra warning directives
|
||||
extra_warnings = {
|
||||
'warn.maybe_uninitialized': True,
|
||||
'warn.unreachable': True,
|
||||
'warn.unused': True,
|
||||
}
|
||||
|
||||
def one_of(*args):
|
||||
def validate(name, value):
|
||||
if value not in args:
|
||||
raise ValueError("%s directive must be one of %s, got '%s'" % (
|
||||
name, args, value))
|
||||
else:
|
||||
return value
|
||||
return validate
|
||||
|
||||
|
||||
def normalise_encoding_name(option_name, encoding):
|
||||
"""
|
||||
>>> normalise_encoding_name('c_string_encoding', 'ascii')
|
||||
'ascii'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'AsCIi')
|
||||
'ascii'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'us-ascii')
|
||||
'ascii'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'utF8')
|
||||
'utf8'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'utF-8')
|
||||
'utf8'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'deFAuLT')
|
||||
'default'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'default')
|
||||
'default'
|
||||
>>> normalise_encoding_name('c_string_encoding', 'SeriousLyNoSuch--Encoding')
|
||||
'SeriousLyNoSuch--Encoding'
|
||||
"""
|
||||
if not encoding:
|
||||
return ''
|
||||
if encoding.lower() in ('default', 'ascii', 'utf8'):
|
||||
return encoding.lower()
|
||||
import codecs
|
||||
try:
|
||||
decoder = codecs.getdecoder(encoding)
|
||||
except LookupError:
|
||||
return encoding # may exists at runtime ...
|
||||
for name in ('ascii', 'utf8'):
|
||||
if codecs.getdecoder(name) == decoder:
|
||||
return name
|
||||
return encoding
|
||||
|
||||
|
||||
# Override types possibilities above, if needed
|
||||
directive_types = {
|
||||
'language_level': str, # values can be None/2/3/'3str', where None == 2+warning
|
||||
'auto_pickle': bool,
|
||||
'locals': dict,
|
||||
'final' : bool, # final cdef classes and methods
|
||||
'nogil' : bool,
|
||||
'internal' : bool, # cdef class visibility in the module dict
|
||||
'infer_types' : bool, # values can be True/None/False
|
||||
'binding' : bool,
|
||||
'cfunc' : None, # decorators do not take directive value
|
||||
'ccall' : None,
|
||||
'inline' : None,
|
||||
'staticmethod' : None,
|
||||
'cclass' : None,
|
||||
'no_gc_clear' : bool,
|
||||
'no_gc' : bool,
|
||||
'returns' : type,
|
||||
'exceptval': type, # actually (type, check=True/False), but has its own parser
|
||||
'set_initial_path': str,
|
||||
'freelist': int,
|
||||
'c_string_type': one_of('bytes', 'bytearray', 'str', 'unicode'),
|
||||
'c_string_encoding': normalise_encoding_name,
|
||||
}
|
||||
|
||||
for key, val in _directive_defaults.items():
|
||||
if key not in directive_types:
|
||||
directive_types[key] = type(val)
|
||||
|
||||
directive_scopes = { # defaults to available everywhere
|
||||
# 'module', 'function', 'class', 'with statement'
|
||||
'auto_pickle': ('module', 'cclass'),
|
||||
'final' : ('cclass', 'function'),
|
||||
'nogil' : ('function', 'with statement'),
|
||||
'inline' : ('function',),
|
||||
'cfunc' : ('function', 'with statement'),
|
||||
'ccall' : ('function', 'with statement'),
|
||||
'returns' : ('function',),
|
||||
'exceptval' : ('function',),
|
||||
'locals' : ('function',),
|
||||
'staticmethod' : ('function',), # FIXME: analysis currently lacks more specific function scope
|
||||
'no_gc_clear' : ('cclass',),
|
||||
'no_gc' : ('cclass',),
|
||||
'internal' : ('cclass',),
|
||||
'cclass' : ('class', 'cclass', 'with statement'),
|
||||
'autotestdict' : ('module',),
|
||||
'autotestdict.all' : ('module',),
|
||||
'autotestdict.cdef' : ('module',),
|
||||
'set_initial_path' : ('module',),
|
||||
'test_assert_path_exists' : ('function', 'class', 'cclass'),
|
||||
'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
|
||||
'freelist': ('cclass',),
|
||||
'emit_code_comments': ('module',),
|
||||
'annotation_typing': ('module',), # FIXME: analysis currently lacks more specific function scope
|
||||
# Avoid scope-specific to/from_py_functions for c_string.
|
||||
'c_string_type': ('module',),
|
||||
'c_string_encoding': ('module',),
|
||||
'type_version_tag': ('module', 'cclass'),
|
||||
'language_level': ('module',),
|
||||
# globals() could conceivably be controlled at a finer granularity,
|
||||
# but that would complicate the implementation
|
||||
'old_style_globals': ('module',),
|
||||
'np_pythran': ('module',),
|
||||
'fast_gil': ('module',),
|
||||
'iterable_coroutine': ('module', 'function'),
|
||||
}
|
||||
|
||||
|
||||
def parse_directive_value(name, value, relaxed_bool=False):
|
||||
"""
|
||||
Parses value as an option value for the given name and returns
|
||||
the interpreted value. None is returned if the option does not exist.
|
||||
|
||||
>>> print(parse_directive_value('nonexisting', 'asdf asdfd'))
|
||||
None
|
||||
>>> parse_directive_value('boundscheck', 'True')
|
||||
True
|
||||
>>> parse_directive_value('boundscheck', 'true')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: boundscheck directive must be set to True or False, got 'true'
|
||||
|
||||
>>> parse_directive_value('c_string_encoding', 'us-ascii')
|
||||
'ascii'
|
||||
>>> parse_directive_value('c_string_type', 'str')
|
||||
'str'
|
||||
>>> parse_directive_value('c_string_type', 'bytes')
|
||||
'bytes'
|
||||
>>> parse_directive_value('c_string_type', 'bytearray')
|
||||
'bytearray'
|
||||
>>> parse_directive_value('c_string_type', 'unicode')
|
||||
'unicode'
|
||||
>>> parse_directive_value('c_string_type', 'unnicode')
|
||||
Traceback (most recent call last):
|
||||
ValueError: c_string_type directive must be one of ('bytes', 'bytearray', 'str', 'unicode'), got 'unnicode'
|
||||
"""
|
||||
type = directive_types.get(name)
|
||||
if not type:
|
||||
return None
|
||||
orig_value = value
|
||||
if type is bool:
|
||||
value = str(value)
|
||||
if value == 'True':
|
||||
return True
|
||||
if value == 'False':
|
||||
return False
|
||||
if relaxed_bool:
|
||||
value = value.lower()
|
||||
if value in ("true", "yes"):
|
||||
return True
|
||||
elif value in ("false", "no"):
|
||||
return False
|
||||
raise ValueError("%s directive must be set to True or False, got '%s'" % (
|
||||
name, orig_value))
|
||||
elif type is int:
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
raise ValueError("%s directive must be set to an integer, got '%s'" % (
|
||||
name, orig_value))
|
||||
elif type is str:
|
||||
return str(value)
|
||||
elif callable(type):
|
||||
return type(name, value)
|
||||
else:
|
||||
assert False
|
||||
|
||||
|
||||
def parse_directive_list(s, relaxed_bool=False, ignore_unknown=False,
|
||||
current_settings=None):
|
||||
"""
|
||||
Parses a comma-separated list of pragma options. Whitespace
|
||||
is not considered.
|
||||
|
||||
>>> parse_directive_list(' ')
|
||||
{}
|
||||
>>> (parse_directive_list('boundscheck=True') ==
|
||||
... {'boundscheck': True})
|
||||
True
|
||||
>>> parse_directive_list(' asdf')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Expected "=" in option "asdf"
|
||||
>>> parse_directive_list('boundscheck=hey')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: boundscheck directive must be set to True or False, got 'hey'
|
||||
>>> parse_directive_list('unknown=True')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Unknown option: "unknown"
|
||||
>>> warnings = parse_directive_list('warn.all=True')
|
||||
>>> len(warnings) > 1
|
||||
True
|
||||
>>> sum(warnings.values()) == len(warnings) # all true.
|
||||
True
|
||||
"""
|
||||
if current_settings is None:
|
||||
result = {}
|
||||
else:
|
||||
result = current_settings
|
||||
for item in s.split(','):
|
||||
item = item.strip()
|
||||
if not item:
|
||||
continue
|
||||
if '=' not in item:
|
||||
raise ValueError('Expected "=" in option "%s"' % item)
|
||||
name, value = [s.strip() for s in item.strip().split('=', 1)]
|
||||
if name not in _directive_defaults:
|
||||
found = False
|
||||
if name.endswith('.all'):
|
||||
prefix = name[:-3]
|
||||
for directive in _directive_defaults:
|
||||
if directive.startswith(prefix):
|
||||
found = True
|
||||
parsed_value = parse_directive_value(directive, value, relaxed_bool=relaxed_bool)
|
||||
result[directive] = parsed_value
|
||||
if not found and not ignore_unknown:
|
||||
raise ValueError('Unknown option: "%s"' % name)
|
||||
else:
|
||||
parsed_value = parse_directive_value(name, value, relaxed_bool=relaxed_bool)
|
||||
result[name] = parsed_value
|
||||
return result
|
||||
|
||||
|
||||
def parse_variable_value(value):
|
||||
"""
|
||||
Parses value as an option value for the given name and returns
|
||||
the interpreted value.
|
||||
|
||||
>>> parse_variable_value('True')
|
||||
True
|
||||
>>> parse_variable_value('true')
|
||||
'true'
|
||||
>>> parse_variable_value('us-ascii')
|
||||
'us-ascii'
|
||||
>>> parse_variable_value('str')
|
||||
'str'
|
||||
>>> parse_variable_value('123')
|
||||
123
|
||||
>>> parse_variable_value('1.23')
|
||||
1.23
|
||||
|
||||
"""
|
||||
if value == "True":
|
||||
return True
|
||||
elif value == "False":
|
||||
return False
|
||||
elif value == "None":
|
||||
return None
|
||||
elif value.isdigit():
|
||||
return int(value)
|
||||
else:
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
# Not a float
|
||||
pass
|
||||
return value
|
||||
|
||||
|
||||
def parse_compile_time_env(s, current_settings=None):
|
||||
"""
|
||||
Parses a comma-separated list of pragma options. Whitespace
|
||||
is not considered.
|
||||
|
||||
>>> parse_compile_time_env(' ')
|
||||
{}
|
||||
>>> (parse_compile_time_env('HAVE_OPENMP=True') ==
|
||||
... {'HAVE_OPENMP': True})
|
||||
True
|
||||
>>> parse_compile_time_env(' asdf')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Expected "=" in option "asdf"
|
||||
>>> parse_compile_time_env('NUM_THREADS=4') == {'NUM_THREADS': 4}
|
||||
True
|
||||
>>> parse_compile_time_env('unknown=anything') == {'unknown': 'anything'}
|
||||
True
|
||||
"""
|
||||
if current_settings is None:
|
||||
result = {}
|
||||
else:
|
||||
result = current_settings
|
||||
for item in s.split(','):
|
||||
item = item.strip()
|
||||
if not item:
|
||||
continue
|
||||
if '=' not in item:
|
||||
raise ValueError('Expected "=" in option "%s"' % item)
|
||||
name, value = [s.strip() for s in item.split('=', 1)]
|
||||
result[name] = parse_variable_value(value)
|
||||
return result
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
cimport cython
|
||||
|
||||
from .Visitor cimport (
|
||||
CythonTransform, VisitorTransform, TreeVisitor,
|
||||
ScopeTrackingTransform, EnvTransform)
|
||||
|
||||
cdef class SkipDeclarations: # (object):
|
||||
pass
|
||||
|
||||
cdef class NormalizeTree(CythonTransform):
|
||||
cdef bint is_in_statlist
|
||||
cdef bint is_in_expr
|
||||
cpdef visit_StatNode(self, node, is_listcontainer=*)
|
||||
|
||||
cdef class PostParse(ScopeTrackingTransform):
|
||||
cdef dict specialattribute_handlers
|
||||
cdef size_t lambda_counter
|
||||
cdef size_t genexpr_counter
|
||||
cdef _visit_assignment_node(self, node, list expr_list)
|
||||
|
||||
|
||||
#def eliminate_rhs_duplicates(list expr_list_list, list ref_node_sequence)
|
||||
#def sort_common_subsequences(list items)
|
||||
@cython.locals(starred_targets=Py_ssize_t, lhs_size=Py_ssize_t, rhs_size=Py_ssize_t)
|
||||
cdef flatten_parallel_assignments(list input, list output)
|
||||
cdef map_starred_assignment(list lhs_targets, list starred_assignments, list lhs_args, list rhs_args)
|
||||
|
||||
#class PxdPostParse(CythonTransform, SkipDeclarations):
|
||||
#class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
|
||||
#class WithTransform(CythonTransform, SkipDeclarations):
|
||||
#class DecoratorTransform(CythonTransform, SkipDeclarations):
|
||||
|
||||
#class AnalyseDeclarationsTransform(EnvTransform):
|
||||
|
||||
cdef class AnalyseExpressionsTransform(CythonTransform):
|
||||
pass
|
||||
|
||||
cdef class ExpandInplaceOperators(EnvTransform):
|
||||
pass
|
||||
|
||||
cdef class AlignFunctionDefinitions(CythonTransform):
|
||||
cdef dict directives
|
||||
cdef set imported_names
|
||||
cdef object scope
|
||||
|
||||
@cython.final
|
||||
cdef class YieldNodeCollector(TreeVisitor):
|
||||
cdef public list yields
|
||||
cdef public list returns
|
||||
cdef public list finallys
|
||||
cdef public list excepts
|
||||
cdef public bint has_return_value
|
||||
cdef public bint has_yield
|
||||
cdef public bint has_await
|
||||
|
||||
@cython.final
|
||||
cdef class MarkClosureVisitor(CythonTransform):
|
||||
cdef bint needs_closure
|
||||
|
||||
@cython.final
|
||||
cdef class CreateClosureClasses(CythonTransform):
|
||||
cdef list path
|
||||
cdef bint in_lambda
|
||||
cdef module_scope
|
||||
cdef generator_class
|
||||
|
||||
cdef create_class_from_scope(self, node, target_module_scope, inner_node=*)
|
||||
cdef find_entries_used_in_closures(self, node)
|
||||
|
||||
#cdef class InjectGilHandling(VisitorTransform, SkipDeclarations):
|
||||
# cdef bint nogil
|
||||
|
||||
cdef class GilCheck(VisitorTransform):
|
||||
cdef list env_stack
|
||||
cdef bint nogil
|
||||
cdef bint nogil_declarator_only
|
||||
|
||||
cdef class TransformBuiltinMethods(EnvTransform):
|
||||
cdef visit_cython_attribute(self, node)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,199 @@
|
||||
# We declare all of these here to type the first argument.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
cimport cython
|
||||
from .Scanning cimport PyrexScanner
|
||||
|
||||
ctypedef object (*p_sub_expr_func)(PyrexScanner obj)
|
||||
|
||||
# entry points
|
||||
|
||||
cpdef p_module(PyrexScanner s, pxd, full_module_name, ctx=*)
|
||||
cpdef p_code(PyrexScanner s, level= *, ctx=*)
|
||||
|
||||
# internal parser states
|
||||
|
||||
cdef p_ident(PyrexScanner s, message =*)
|
||||
cdef p_ident_list(PyrexScanner s)
|
||||
|
||||
cdef tuple p_binop_operator(PyrexScanner s)
|
||||
cdef p_binop_expr(PyrexScanner s, ops, p_sub_expr_func p_sub_expr)
|
||||
cdef p_lambdef(PyrexScanner s, bint allow_conditional=*)
|
||||
cdef p_lambdef_nocond(PyrexScanner s)
|
||||
cdef p_test(PyrexScanner s)
|
||||
cdef p_test_nocond(PyrexScanner s)
|
||||
cdef p_or_test(PyrexScanner s)
|
||||
cdef p_rassoc_binop_expr(PyrexScanner s, ops, p_sub_expr_func p_subexpr)
|
||||
cdef p_and_test(PyrexScanner s)
|
||||
cdef p_not_test(PyrexScanner s)
|
||||
cdef p_comparison(PyrexScanner s)
|
||||
cdef p_test_or_starred_expr(PyrexScanner s)
|
||||
cdef p_starred_expr(PyrexScanner s)
|
||||
cdef p_cascaded_cmp(PyrexScanner s)
|
||||
cdef p_cmp_op(PyrexScanner s)
|
||||
cdef p_bit_expr(PyrexScanner s)
|
||||
cdef p_xor_expr(PyrexScanner s)
|
||||
cdef p_and_expr(PyrexScanner s)
|
||||
cdef p_shift_expr(PyrexScanner s)
|
||||
cdef p_arith_expr(PyrexScanner s)
|
||||
cdef p_term(PyrexScanner s)
|
||||
cdef p_factor(PyrexScanner s)
|
||||
cdef _p_factor(PyrexScanner s)
|
||||
cdef p_typecast(PyrexScanner s)
|
||||
cdef p_sizeof(PyrexScanner s)
|
||||
cdef p_yield_expression(PyrexScanner s)
|
||||
cdef p_yield_statement(PyrexScanner s)
|
||||
cdef p_async_statement(PyrexScanner s, ctx, decorators)
|
||||
cdef p_power(PyrexScanner s)
|
||||
cdef p_new_expr(PyrexScanner s)
|
||||
cdef p_trailer(PyrexScanner s, node1)
|
||||
cdef p_call_parse_args(PyrexScanner s, bint allow_genexp = *)
|
||||
cdef p_call_build_packed_args(pos, positional_args, keyword_args)
|
||||
cdef p_call(PyrexScanner s, function)
|
||||
cdef p_index(PyrexScanner s, base)
|
||||
cdef tuple p_subscript_list(PyrexScanner s)
|
||||
cdef p_subscript(PyrexScanner s)
|
||||
cdef p_slice_element(PyrexScanner s, follow_set)
|
||||
cdef expect_ellipsis(PyrexScanner s)
|
||||
cdef make_slice_nodes(pos, subscripts)
|
||||
cpdef make_slice_node(pos, start, stop = *, step = *)
|
||||
cdef p_atom(PyrexScanner s)
|
||||
@cython.locals(value=unicode)
|
||||
cdef p_int_literal(PyrexScanner s)
|
||||
cdef p_name(PyrexScanner s, name)
|
||||
cdef wrap_compile_time_constant(pos, value)
|
||||
cdef p_cat_string_literal(PyrexScanner s)
|
||||
cdef p_opt_string_literal(PyrexScanner s, required_type=*)
|
||||
cdef bint check_for_non_ascii_characters(unicode string)
|
||||
@cython.locals(systr=unicode, is_python3_source=bint, is_raw=bint)
|
||||
cdef p_string_literal(PyrexScanner s, kind_override=*)
|
||||
cdef _append_escape_sequence(kind, builder, unicode escape_sequence, PyrexScanner s)
|
||||
cdef tuple _f_string_error_pos(pos, string, Py_ssize_t i)
|
||||
@cython.locals(i=Py_ssize_t, size=Py_ssize_t, c=Py_UCS4, next_start=Py_ssize_t)
|
||||
cdef list p_f_string(PyrexScanner s, unicode_value, pos, bint is_raw)
|
||||
@cython.locals(i=Py_ssize_t, size=Py_ssize_t, c=Py_UCS4, quote_char=Py_UCS4, NO_CHAR=Py_UCS4)
|
||||
cdef tuple p_f_string_expr(PyrexScanner s, unicode_value, pos, Py_ssize_t starting_index, bint is_raw)
|
||||
cdef p_list_maker(PyrexScanner s)
|
||||
cdef p_comp_iter(PyrexScanner s, body)
|
||||
cdef p_comp_for(PyrexScanner s, body)
|
||||
cdef p_comp_if(PyrexScanner s, body)
|
||||
cdef p_dict_or_set_maker(PyrexScanner s)
|
||||
cdef p_backquote_expr(PyrexScanner s)
|
||||
cdef p_simple_expr_list(PyrexScanner s, expr=*)
|
||||
cdef p_test_or_starred_expr_list(PyrexScanner s, expr=*)
|
||||
cdef p_testlist(PyrexScanner s)
|
||||
cdef p_testlist_star_expr(PyrexScanner s)
|
||||
cdef p_testlist_comp(PyrexScanner s)
|
||||
cdef p_genexp(PyrexScanner s, expr)
|
||||
|
||||
#-------------------------------------------------------
|
||||
#
|
||||
# Statements
|
||||
#
|
||||
#-------------------------------------------------------
|
||||
|
||||
cdef p_global_statement(PyrexScanner s)
|
||||
cdef p_nonlocal_statement(PyrexScanner s)
|
||||
cdef p_expression_or_assignment(PyrexScanner s)
|
||||
cdef p_print_statement(PyrexScanner s)
|
||||
cdef p_exec_statement(PyrexScanner s)
|
||||
cdef p_del_statement(PyrexScanner s)
|
||||
cdef p_pass_statement(PyrexScanner s, bint with_newline = *)
|
||||
cdef p_break_statement(PyrexScanner s)
|
||||
cdef p_continue_statement(PyrexScanner s)
|
||||
cdef p_return_statement(PyrexScanner s)
|
||||
cdef p_raise_statement(PyrexScanner s)
|
||||
cdef p_import_statement(PyrexScanner s)
|
||||
cdef p_from_import_statement(PyrexScanner s, bint first_statement = *)
|
||||
cdef p_imported_name(PyrexScanner s, bint is_cimport)
|
||||
cdef p_dotted_name(PyrexScanner s, bint as_allowed)
|
||||
cdef p_as_name(PyrexScanner s)
|
||||
cdef p_assert_statement(PyrexScanner s)
|
||||
cdef p_if_statement(PyrexScanner s)
|
||||
cdef p_if_clause(PyrexScanner s)
|
||||
cdef p_else_clause(PyrexScanner s)
|
||||
cdef p_while_statement(PyrexScanner s)
|
||||
cdef p_for_statement(PyrexScanner s, bint is_async=*)
|
||||
cdef dict p_for_bounds(PyrexScanner s, bint allow_testlist=*, bint is_async=*)
|
||||
cdef p_for_from_relation(PyrexScanner s)
|
||||
cdef p_for_from_step(PyrexScanner s)
|
||||
cdef p_target(PyrexScanner s, terminator)
|
||||
cdef p_for_target(PyrexScanner s)
|
||||
cdef p_for_iterator(PyrexScanner s, bint allow_testlist=*, bint is_async=*)
|
||||
cdef p_try_statement(PyrexScanner s)
|
||||
cdef p_except_clause(PyrexScanner s)
|
||||
cdef p_include_statement(PyrexScanner s, ctx)
|
||||
cdef p_with_statement(PyrexScanner s)
|
||||
cdef p_with_items(PyrexScanner s, bint is_async=*)
|
||||
cdef p_with_template(PyrexScanner s)
|
||||
cdef p_simple_statement(PyrexScanner s, bint first_statement = *)
|
||||
cdef p_simple_statement_list(PyrexScanner s, ctx, bint first_statement = *)
|
||||
cdef p_compile_time_expr(PyrexScanner s)
|
||||
cdef p_DEF_statement(PyrexScanner s)
|
||||
cdef p_IF_statement(PyrexScanner s, ctx)
|
||||
cdef p_statement(PyrexScanner s, ctx, bint first_statement = *)
|
||||
cdef p_statement_list(PyrexScanner s, ctx, bint first_statement = *)
|
||||
cdef p_suite(PyrexScanner s, ctx = *)
|
||||
cdef tuple p_suite_with_docstring(PyrexScanner s, ctx, bint with_doc_only=*)
|
||||
cdef tuple _extract_docstring(node)
|
||||
cdef p_positional_and_keyword_args(PyrexScanner s, end_sy_set, templates = *)
|
||||
|
||||
cpdef p_c_base_type(PyrexScanner s, bint self_flag = *, bint nonempty = *, templates = *)
|
||||
cdef p_calling_convention(PyrexScanner s)
|
||||
cdef p_c_complex_base_type(PyrexScanner s, templates = *)
|
||||
cdef p_c_simple_base_type(PyrexScanner s, bint self_flag, bint nonempty, templates = *)
|
||||
cdef p_buffer_or_template(PyrexScanner s, base_type_node, templates)
|
||||
cdef p_bracketed_base_type(PyrexScanner s, base_type_node, nonempty, empty)
|
||||
cdef is_memoryviewslice_access(PyrexScanner s)
|
||||
cdef p_memoryviewslice_access(PyrexScanner s, base_type_node)
|
||||
cdef bint looking_at_name(PyrexScanner s) except -2
|
||||
cdef object looking_at_expr(PyrexScanner s)# except -2
|
||||
cdef bint looking_at_base_type(PyrexScanner s) except -2
|
||||
cdef bint looking_at_dotted_name(PyrexScanner s) except -2
|
||||
cdef bint looking_at_call(PyrexScanner s) except -2
|
||||
cdef p_sign_and_longness(PyrexScanner s)
|
||||
cdef p_opt_cname(PyrexScanner s)
|
||||
cpdef p_c_declarator(PyrexScanner s, ctx = *, bint empty = *, bint is_type = *, bint cmethod_flag = *,
|
||||
bint assignable = *, bint nonempty = *,
|
||||
bint calling_convention_allowed = *)
|
||||
cdef p_c_array_declarator(PyrexScanner s, base)
|
||||
cdef p_c_func_declarator(PyrexScanner s, pos, ctx, base, bint cmethod_flag)
|
||||
cdef p_c_simple_declarator(PyrexScanner s, ctx, bint empty, bint is_type, bint cmethod_flag,
|
||||
bint assignable, bint nonempty)
|
||||
cdef p_nogil(PyrexScanner s)
|
||||
cdef p_with_gil(PyrexScanner s)
|
||||
cdef p_exception_value_clause(PyrexScanner s)
|
||||
cpdef p_c_arg_list(PyrexScanner s, ctx = *, bint in_pyfunc = *, bint cmethod_flag = *,
|
||||
bint nonempty_declarators = *, bint kw_only = *, bint annotated = *)
|
||||
cdef p_optional_ellipsis(PyrexScanner s)
|
||||
cdef p_c_arg_decl(PyrexScanner s, ctx, in_pyfunc, bint cmethod_flag = *, bint nonempty = *, bint kw_only = *, bint annotated = *)
|
||||
cdef p_api(PyrexScanner s)
|
||||
cdef p_cdef_statement(PyrexScanner s, ctx)
|
||||
cdef p_cdef_block(PyrexScanner s, ctx)
|
||||
cdef p_cdef_extern_block(PyrexScanner s, pos, ctx)
|
||||
cdef p_c_enum_definition(PyrexScanner s, pos, ctx)
|
||||
cdef p_c_enum_line(PyrexScanner s, ctx, list items)
|
||||
cdef p_c_enum_item(PyrexScanner s, ctx, list items)
|
||||
cdef p_c_struct_or_union_definition(PyrexScanner s, pos, ctx)
|
||||
cdef p_fused_definition(PyrexScanner s, pos, ctx)
|
||||
cdef p_struct_enum(PyrexScanner s, pos, ctx)
|
||||
cdef p_visibility(PyrexScanner s, prev_visibility)
|
||||
cdef p_c_modifiers(PyrexScanner s)
|
||||
cdef p_c_func_or_var_declaration(PyrexScanner s, pos, ctx)
|
||||
cdef p_ctypedef_statement(PyrexScanner s, ctx)
|
||||
cdef p_decorators(PyrexScanner s)
|
||||
cdef _reject_cdef_modifier_in_py(PyrexScanner s, name)
|
||||
cdef p_def_statement(PyrexScanner s, list decorators=*, bint is_async_def=*)
|
||||
cdef p_varargslist(PyrexScanner s, terminator=*, bint annotated = *)
|
||||
cdef p_py_arg_decl(PyrexScanner s, bint annotated = *)
|
||||
cdef p_class_statement(PyrexScanner s, decorators)
|
||||
cdef p_c_class_definition(PyrexScanner s, pos, ctx)
|
||||
cdef tuple p_c_class_options(PyrexScanner s)
|
||||
cdef p_property_decl(PyrexScanner s)
|
||||
cdef p_doc_string(PyrexScanner s)
|
||||
cdef p_ignorable_statement(PyrexScanner s)
|
||||
cdef dict p_compiler_directive_comments(PyrexScanner s)
|
||||
cdef p_template_definition(PyrexScanner s)
|
||||
cdef p_cpp_class_definition(PyrexScanner s, pos, ctx)
|
||||
cdef p_cpp_class_attribute(PyrexScanner s, ctx)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,369 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import itertools
|
||||
from time import time
|
||||
|
||||
from . import Errors
|
||||
from . import DebugFlags
|
||||
from . import Options
|
||||
from .Errors import CompileError, InternalError, AbortError
|
||||
from . import Naming
|
||||
|
||||
#
|
||||
# Really small pipeline stages
|
||||
#
|
||||
def dumptree(t):
|
||||
# For quick debugging in pipelines
|
||||
print(t.dump())
|
||||
return t
|
||||
|
||||
def abort_on_errors(node):
|
||||
# Stop the pipeline if there are any errors.
|
||||
if Errors.num_errors != 0:
|
||||
raise AbortError("pipeline break")
|
||||
return node
|
||||
|
||||
def parse_stage_factory(context):
|
||||
def parse(compsrc):
|
||||
source_desc = compsrc.source_desc
|
||||
full_module_name = compsrc.full_module_name
|
||||
initial_pos = (source_desc, 1, 0)
|
||||
saved_cimport_from_pyx, Options.cimport_from_pyx = Options.cimport_from_pyx, False
|
||||
scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0)
|
||||
Options.cimport_from_pyx = saved_cimport_from_pyx
|
||||
tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
|
||||
tree.compilation_source = compsrc
|
||||
tree.scope = scope
|
||||
tree.is_pxd = False
|
||||
return tree
|
||||
return parse
|
||||
|
||||
def parse_pxd_stage_factory(context, scope, module_name):
|
||||
def parse(source_desc):
|
||||
tree = context.parse(source_desc, scope, pxd=True,
|
||||
full_module_name=module_name)
|
||||
tree.scope = scope
|
||||
tree.is_pxd = True
|
||||
return tree
|
||||
return parse
|
||||
|
||||
def generate_pyx_code_stage_factory(options, result):
|
||||
def generate_pyx_code_stage(module_node):
|
||||
module_node.process_implementation(options, result)
|
||||
result.compilation_source = module_node.compilation_source
|
||||
return result
|
||||
return generate_pyx_code_stage
|
||||
|
||||
|
||||
def inject_pxd_code_stage_factory(context):
|
||||
def inject_pxd_code_stage(module_node):
|
||||
for name, (statlistnode, scope) in context.pxds.items():
|
||||
module_node.merge_in(statlistnode, scope)
|
||||
return module_node
|
||||
return inject_pxd_code_stage
|
||||
|
||||
|
||||
def use_utility_code_definitions(scope, target, seen=None):
|
||||
if seen is None:
|
||||
seen = set()
|
||||
|
||||
for entry in scope.entries.values():
|
||||
if entry in seen:
|
||||
continue
|
||||
|
||||
seen.add(entry)
|
||||
if entry.used and entry.utility_code_definition:
|
||||
target.use_utility_code(entry.utility_code_definition)
|
||||
for required_utility in entry.utility_code_definition.requires:
|
||||
target.use_utility_code(required_utility)
|
||||
elif entry.as_module:
|
||||
use_utility_code_definitions(entry.as_module, target, seen)
|
||||
|
||||
|
||||
def sort_utility_codes(utilcodes):
|
||||
ranks = {}
|
||||
def get_rank(utilcode):
|
||||
if utilcode not in ranks:
|
||||
ranks[utilcode] = 0 # prevent infinite recursion on circular dependencies
|
||||
original_order = len(ranks)
|
||||
ranks[utilcode] = 1 + min([get_rank(dep) for dep in utilcode.requires or ()] or [-1]) + original_order * 1e-8
|
||||
return ranks[utilcode]
|
||||
for utilcode in utilcodes:
|
||||
get_rank(utilcode)
|
||||
return [utilcode for utilcode, _ in sorted(ranks.items(), key=lambda kv: kv[1])]
|
||||
|
||||
|
||||
def normalize_deps(utilcodes):
|
||||
deps = {}
|
||||
for utilcode in utilcodes:
|
||||
deps[utilcode] = utilcode
|
||||
|
||||
def unify_dep(dep):
|
||||
if dep in deps:
|
||||
return deps[dep]
|
||||
else:
|
||||
deps[dep] = dep
|
||||
return dep
|
||||
|
||||
for utilcode in utilcodes:
|
||||
utilcode.requires = [unify_dep(dep) for dep in utilcode.requires or ()]
|
||||
|
||||
|
||||
def inject_utility_code_stage_factory(context):
|
||||
def inject_utility_code_stage(module_node):
|
||||
module_node.prepare_utility_code()
|
||||
use_utility_code_definitions(context.cython_scope, module_node.scope)
|
||||
module_node.scope.utility_code_list = sort_utility_codes(module_node.scope.utility_code_list)
|
||||
normalize_deps(module_node.scope.utility_code_list)
|
||||
added = []
|
||||
# Note: the list might be extended inside the loop (if some utility code
|
||||
# pulls in other utility code, explicitly or implicitly)
|
||||
for utilcode in module_node.scope.utility_code_list:
|
||||
if utilcode in added:
|
||||
continue
|
||||
added.append(utilcode)
|
||||
if utilcode.requires:
|
||||
for dep in utilcode.requires:
|
||||
if dep not in added and dep not in module_node.scope.utility_code_list:
|
||||
module_node.scope.utility_code_list.append(dep)
|
||||
tree = utilcode.get_tree(cython_scope=context.cython_scope)
|
||||
if tree:
|
||||
module_node.merge_in(tree.body, tree.scope, merge_scope=True)
|
||||
return module_node
|
||||
return inject_utility_code_stage
|
||||
|
||||
|
||||
#
|
||||
# Pipeline factories
|
||||
#
|
||||
|
||||
def create_pipeline(context, mode, exclude_classes=()):
|
||||
assert mode in ('pyx', 'py', 'pxd')
|
||||
from .Visitor import PrintTree
|
||||
from .ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
|
||||
from .ParseTreeTransforms import ForwardDeclareTypes, InjectGilHandling, AnalyseDeclarationsTransform
|
||||
from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes
|
||||
from .ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
|
||||
from .ParseTreeTransforms import TrackNumpyAttributes, InterpretCompilerDirectives, TransformBuiltinMethods
|
||||
from .ParseTreeTransforms import ExpandInplaceOperators, ParallelRangeTransform
|
||||
from .ParseTreeTransforms import CalculateQualifiedNamesTransform
|
||||
from .TypeInference import MarkParallelAssignments, MarkOverflowingArithmetic
|
||||
from .ParseTreeTransforms import AdjustDefByDirectives, AlignFunctionDefinitions
|
||||
from .ParseTreeTransforms import RemoveUnreachableCode, GilCheck
|
||||
from .FlowControl import ControlFlowAnalysis
|
||||
from .AnalysedTreeTransforms import AutoTestDictTransform
|
||||
from .AutoDocTransforms import EmbedSignature
|
||||
from .Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
|
||||
from .Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls
|
||||
from .Optimize import InlineDefNodeCalls
|
||||
from .Optimize import ConstantFolding, FinalOptimizePhase
|
||||
from .Optimize import DropRefcountingTransform
|
||||
from .Optimize import ConsolidateOverflowCheck
|
||||
from .Buffer import IntroduceBufferAuxiliaryVars
|
||||
from .ModuleNode import check_c_declarations, check_c_declarations_pxd
|
||||
|
||||
|
||||
if mode == 'pxd':
|
||||
_check_c_declarations = check_c_declarations_pxd
|
||||
_specific_post_parse = PxdPostParse(context)
|
||||
else:
|
||||
_check_c_declarations = check_c_declarations
|
||||
_specific_post_parse = None
|
||||
|
||||
if mode == 'py':
|
||||
_align_function_definitions = AlignFunctionDefinitions(context)
|
||||
else:
|
||||
_align_function_definitions = None
|
||||
|
||||
# NOTE: This is the "common" parts of the pipeline, which is also
|
||||
# code in pxd files. So it will be run multiple times in a
|
||||
# compilation stage.
|
||||
stages = [
|
||||
NormalizeTree(context),
|
||||
PostParse(context),
|
||||
_specific_post_parse,
|
||||
TrackNumpyAttributes(),
|
||||
InterpretCompilerDirectives(context, context.compiler_directives),
|
||||
ParallelRangeTransform(context),
|
||||
AdjustDefByDirectives(context),
|
||||
WithTransform(context),
|
||||
MarkClosureVisitor(context),
|
||||
_align_function_definitions,
|
||||
RemoveUnreachableCode(context),
|
||||
ConstantFolding(),
|
||||
FlattenInListTransform(),
|
||||
DecoratorTransform(context),
|
||||
ForwardDeclareTypes(context),
|
||||
InjectGilHandling(),
|
||||
AnalyseDeclarationsTransform(context),
|
||||
AutoTestDictTransform(context),
|
||||
EmbedSignature(context),
|
||||
EarlyReplaceBuiltinCalls(context), ## Necessary?
|
||||
TransformBuiltinMethods(context),
|
||||
MarkParallelAssignments(context),
|
||||
ControlFlowAnalysis(context),
|
||||
RemoveUnreachableCode(context),
|
||||
# MarkParallelAssignments(context),
|
||||
MarkOverflowingArithmetic(context),
|
||||
IntroduceBufferAuxiliaryVars(context),
|
||||
_check_c_declarations,
|
||||
InlineDefNodeCalls(context),
|
||||
AnalyseExpressionsTransform(context),
|
||||
FindInvalidUseOfFusedTypes(context),
|
||||
ExpandInplaceOperators(context),
|
||||
IterationTransform(context),
|
||||
SwitchTransform(context),
|
||||
OptimizeBuiltinCalls(context), ## Necessary?
|
||||
CreateClosureClasses(context), ## After all lookups and type inference
|
||||
CalculateQualifiedNamesTransform(context),
|
||||
ConsolidateOverflowCheck(context),
|
||||
DropRefcountingTransform(),
|
||||
FinalOptimizePhase(context),
|
||||
GilCheck(),
|
||||
]
|
||||
filtered_stages = []
|
||||
for s in stages:
|
||||
if s.__class__ not in exclude_classes:
|
||||
filtered_stages.append(s)
|
||||
return filtered_stages
|
||||
|
||||
def create_pyx_pipeline(context, options, result, py=False, exclude_classes=()):
|
||||
if py:
|
||||
mode = 'py'
|
||||
else:
|
||||
mode = 'pyx'
|
||||
test_support = []
|
||||
if options.evaluate_tree_assertions:
|
||||
from ..TestUtils import TreeAssertVisitor
|
||||
test_support.append(TreeAssertVisitor())
|
||||
|
||||
if options.gdb_debug:
|
||||
from ..Debugger import DebugWriter # requires Py2.5+
|
||||
from .ParseTreeTransforms import DebugTransform
|
||||
context.gdb_debug_outputwriter = DebugWriter.CythonDebugWriter(
|
||||
options.output_dir)
|
||||
debug_transform = [DebugTransform(context, options, result)]
|
||||
else:
|
||||
debug_transform = []
|
||||
|
||||
return list(itertools.chain(
|
||||
[parse_stage_factory(context)],
|
||||
create_pipeline(context, mode, exclude_classes=exclude_classes),
|
||||
test_support,
|
||||
[inject_pxd_code_stage_factory(context),
|
||||
inject_utility_code_stage_factory(context),
|
||||
abort_on_errors],
|
||||
debug_transform,
|
||||
[generate_pyx_code_stage_factory(options, result)]))
|
||||
|
||||
def create_pxd_pipeline(context, scope, module_name):
|
||||
from .CodeGeneration import ExtractPxdCode
|
||||
|
||||
# The pxd pipeline ends up with a CCodeWriter containing the
|
||||
# code of the pxd, as well as a pxd scope.
|
||||
return [
|
||||
parse_pxd_stage_factory(context, scope, module_name)
|
||||
] + create_pipeline(context, 'pxd') + [
|
||||
ExtractPxdCode()
|
||||
]
|
||||
|
||||
def create_py_pipeline(context, options, result):
|
||||
return create_pyx_pipeline(context, options, result, py=True)
|
||||
|
||||
def create_pyx_as_pxd_pipeline(context, result):
|
||||
from .ParseTreeTransforms import AlignFunctionDefinitions, \
|
||||
MarkClosureVisitor, WithTransform, AnalyseDeclarationsTransform
|
||||
from .Optimize import ConstantFolding, FlattenInListTransform
|
||||
from .Nodes import StatListNode
|
||||
pipeline = []
|
||||
pyx_pipeline = create_pyx_pipeline(context, context.options, result,
|
||||
exclude_classes=[
|
||||
AlignFunctionDefinitions,
|
||||
MarkClosureVisitor,
|
||||
ConstantFolding,
|
||||
FlattenInListTransform,
|
||||
WithTransform
|
||||
])
|
||||
for stage in pyx_pipeline:
|
||||
pipeline.append(stage)
|
||||
if isinstance(stage, AnalyseDeclarationsTransform):
|
||||
# This is the last stage we need.
|
||||
break
|
||||
def fake_pxd(root):
|
||||
for entry in root.scope.entries.values():
|
||||
if not entry.in_cinclude:
|
||||
entry.defined_in_pxd = 1
|
||||
if entry.name == entry.cname and entry.visibility != 'extern':
|
||||
# Always mangle non-extern cimported entries.
|
||||
entry.cname = entry.scope.mangle(Naming.func_prefix, entry.name)
|
||||
return StatListNode(root.pos, stats=[]), root.scope
|
||||
pipeline.append(fake_pxd)
|
||||
return pipeline
|
||||
|
||||
def insert_into_pipeline(pipeline, transform, before=None, after=None):
|
||||
"""
|
||||
Insert a new transform into the pipeline after or before an instance of
|
||||
the given class. e.g.
|
||||
|
||||
pipeline = insert_into_pipeline(pipeline, transform,
|
||||
after=AnalyseDeclarationsTransform)
|
||||
"""
|
||||
assert before or after
|
||||
|
||||
cls = before or after
|
||||
for i, t in enumerate(pipeline):
|
||||
if isinstance(t, cls):
|
||||
break
|
||||
|
||||
if after:
|
||||
i += 1
|
||||
|
||||
return pipeline[:i] + [transform] + pipeline[i:]
|
||||
|
||||
#
|
||||
# Running a pipeline
|
||||
#
|
||||
|
||||
_pipeline_entry_points = {}
|
||||
|
||||
|
||||
def run_pipeline(pipeline, source, printtree=True):
|
||||
from .Visitor import PrintTree
|
||||
exec_ns = globals().copy() if DebugFlags.debug_verbose_pipeline else None
|
||||
|
||||
def run(phase, data):
|
||||
return phase(data)
|
||||
|
||||
error = None
|
||||
data = source
|
||||
try:
|
||||
try:
|
||||
for phase in pipeline:
|
||||
if phase is not None:
|
||||
if not printtree and isinstance(phase, PrintTree):
|
||||
continue
|
||||
if DebugFlags.debug_verbose_pipeline:
|
||||
t = time()
|
||||
print("Entering pipeline phase %r" % phase)
|
||||
# create a new wrapper for each step to show the name in profiles
|
||||
phase_name = getattr(phase, '__name__', type(phase).__name__)
|
||||
try:
|
||||
run = _pipeline_entry_points[phase_name]
|
||||
except KeyError:
|
||||
exec("def %s(phase, data): return phase(data)" % phase_name, exec_ns)
|
||||
run = _pipeline_entry_points[phase_name] = exec_ns[phase_name]
|
||||
data = run(phase, data)
|
||||
if DebugFlags.debug_verbose_pipeline:
|
||||
print(" %.3f seconds" % (time() - t))
|
||||
except CompileError as err:
|
||||
# err is set
|
||||
Errors.report_error(err, use_stack=False)
|
||||
error = err
|
||||
except InternalError as err:
|
||||
# Only raise if there was not an earlier error
|
||||
if Errors.num_errors == 0:
|
||||
raise
|
||||
error = err
|
||||
except AbortError as err:
|
||||
error = err
|
||||
return (error, data)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,227 @@
|
||||
# cython: language_level=3
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .PyrexTypes import CType, CTypedefType, CStructOrUnionType
|
||||
|
||||
import cython
|
||||
|
||||
try:
|
||||
import pythran
|
||||
pythran_is_pre_0_9 = tuple(map(int, pythran.__version__.split('.')[0:2])) < (0, 9)
|
||||
pythran_is_pre_0_9_6 = tuple(map(int, pythran.__version__.split('.')[0:3])) < (0, 9, 6)
|
||||
except ImportError:
|
||||
pythran = None
|
||||
pythran_is_pre_0_9 = True
|
||||
pythran_is_pre_0_9_6 = True
|
||||
|
||||
if pythran_is_pre_0_9_6:
|
||||
pythran_builtins = '__builtin__'
|
||||
else:
|
||||
pythran_builtins = 'builtins'
|
||||
|
||||
|
||||
# Pythran/Numpy specific operations
|
||||
|
||||
def has_np_pythran(env):
|
||||
if env is None:
|
||||
return False
|
||||
directives = getattr(env, 'directives', None)
|
||||
return (directives and directives.get('np_pythran', False))
|
||||
|
||||
@cython.ccall
|
||||
def is_pythran_supported_dtype(type_):
|
||||
if isinstance(type_, CTypedefType):
|
||||
return is_pythran_supported_type(type_.typedef_base_type)
|
||||
return type_.is_numeric
|
||||
|
||||
|
||||
def pythran_type(Ty, ptype="ndarray"):
|
||||
if Ty.is_buffer:
|
||||
ndim,dtype = Ty.ndim, Ty.dtype
|
||||
if isinstance(dtype, CStructOrUnionType):
|
||||
ctype = dtype.cname
|
||||
elif isinstance(dtype, CType):
|
||||
ctype = dtype.sign_and_name()
|
||||
elif isinstance(dtype, CTypedefType):
|
||||
ctype = dtype.typedef_cname
|
||||
else:
|
||||
raise ValueError("unsupported type %s!" % dtype)
|
||||
if pythran_is_pre_0_9:
|
||||
return "pythonic::types::%s<%s,%d>" % (ptype,ctype, ndim)
|
||||
else:
|
||||
return "pythonic::types::%s<%s,pythonic::types::pshape<%s>>" % (ptype,ctype, ",".join(("long",)*ndim))
|
||||
if Ty.is_pythran_expr:
|
||||
return Ty.pythran_type
|
||||
#if Ty.is_none:
|
||||
# return "decltype(pythonic::builtins::None)"
|
||||
if Ty.is_numeric:
|
||||
return Ty.sign_and_name()
|
||||
raise ValueError("unsupported pythran type %s (%s)" % (Ty, type(Ty)))
|
||||
|
||||
|
||||
@cython.cfunc
|
||||
def type_remove_ref(ty):
|
||||
return "typename std::remove_reference<%s>::type" % ty
|
||||
|
||||
|
||||
def pythran_binop_type(op, tA, tB):
|
||||
if op == '**':
|
||||
return 'decltype(pythonic::numpy::functor::power{}(std::declval<%s>(), std::declval<%s>()))' % (
|
||||
pythran_type(tA), pythran_type(tB))
|
||||
else:
|
||||
return "decltype(std::declval<%s>() %s std::declval<%s>())" % (
|
||||
pythran_type(tA), op, pythran_type(tB))
|
||||
|
||||
|
||||
def pythran_unaryop_type(op, type_):
|
||||
return "decltype(%sstd::declval<%s>())" % (
|
||||
op, pythran_type(type_))
|
||||
|
||||
|
||||
@cython.cfunc
|
||||
def _index_access(index_code, indices):
|
||||
indexing = ",".join([index_code(idx) for idx in indices])
|
||||
return ('[%s]' if len(indices) == 1 else '(%s)') % indexing
|
||||
|
||||
|
||||
def _index_type_code(index_with_type):
|
||||
idx, index_type = index_with_type
|
||||
if idx.is_slice:
|
||||
n = 2 + int(not idx.step.is_none)
|
||||
return "pythonic::%s::functor::slice{}(%s)" % (
|
||||
pythran_builtins,
|
||||
",".join(["0"]*n))
|
||||
elif index_type.is_int:
|
||||
return "std::declval<%s>()" % index_type.sign_and_name()
|
||||
elif index_type.is_pythran_expr:
|
||||
return "std::declval<%s>()" % index_type.pythran_type
|
||||
raise ValueError("unsupported indexing type %s!" % index_type)
|
||||
|
||||
|
||||
def _index_code(idx):
|
||||
if idx.is_slice:
|
||||
values = idx.start, idx.stop, idx.step
|
||||
if idx.step.is_none:
|
||||
func = "contiguous_slice"
|
||||
values = values[:2]
|
||||
else:
|
||||
func = "slice"
|
||||
return "pythonic::types::%s(%s)" % (
|
||||
func, ",".join((v.pythran_result() for v in values)))
|
||||
elif idx.type.is_int:
|
||||
return to_pythran(idx)
|
||||
elif idx.type.is_pythran_expr:
|
||||
return idx.pythran_result()
|
||||
raise ValueError("unsupported indexing type %s" % idx.type)
|
||||
|
||||
|
||||
def pythran_indexing_type(type_, indices):
|
||||
return type_remove_ref("decltype(std::declval<%s>()%s)" % (
|
||||
pythran_type(type_),
|
||||
_index_access(_index_type_code, indices),
|
||||
))
|
||||
|
||||
|
||||
def pythran_indexing_code(indices):
|
||||
return _index_access(_index_code, indices)
|
||||
|
||||
def np_func_to_list(func):
|
||||
if not func.is_numpy_attribute:
|
||||
return []
|
||||
return np_func_to_list(func.obj) + [func.attribute]
|
||||
|
||||
if pythran is None:
|
||||
def pythran_is_numpy_func_supported(name):
|
||||
return False
|
||||
else:
|
||||
def pythran_is_numpy_func_supported(func):
|
||||
CurF = pythran.tables.MODULES['numpy']
|
||||
FL = np_func_to_list(func)
|
||||
for F in FL:
|
||||
CurF = CurF.get(F, None)
|
||||
if CurF is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def pythran_functor(func):
|
||||
func = np_func_to_list(func)
|
||||
submodules = "::".join(func[:-1] + ["functor"])
|
||||
return "pythonic::numpy::%s::%s" % (submodules, func[-1])
|
||||
|
||||
def pythran_func_type(func, args):
|
||||
args = ",".join(("std::declval<%s>()" % pythran_type(a.type) for a in args))
|
||||
return "decltype(%s{}(%s))" % (pythran_functor(func), args)
|
||||
|
||||
|
||||
@cython.ccall
|
||||
def to_pythran(op, ptype=None):
|
||||
op_type = op.type
|
||||
if op_type.is_int:
|
||||
# Make sure that integer literals always have exactly the type that the templates expect.
|
||||
return op_type.cast_code(op.result())
|
||||
if is_type(op_type, ["is_pythran_expr", "is_numeric", "is_float", "is_complex"]):
|
||||
return op.result()
|
||||
if op.is_none:
|
||||
return "pythonic::%s::None" % pythran_builtins
|
||||
if ptype is None:
|
||||
ptype = pythran_type(op_type)
|
||||
|
||||
assert op.type.is_pyobject
|
||||
return "from_python<%s>(%s)" % (ptype, op.py_result())
|
||||
|
||||
|
||||
@cython.cfunc
|
||||
def is_type(type_, types):
|
||||
for attr in types:
|
||||
if getattr(type_, attr, False):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_pythran_supported_node_or_none(node):
|
||||
return node.is_none or is_pythran_supported_type(node.type)
|
||||
|
||||
|
||||
@cython.ccall
|
||||
def is_pythran_supported_type(type_):
|
||||
pythran_supported = (
|
||||
"is_pythran_expr", "is_int", "is_numeric", "is_float", "is_none", "is_complex")
|
||||
return is_type(type_, pythran_supported) or is_pythran_expr(type_)
|
||||
|
||||
|
||||
def is_pythran_supported_operation_type(type_):
|
||||
pythran_supported = (
|
||||
"is_pythran_expr", "is_int", "is_numeric", "is_float", "is_complex")
|
||||
return is_type(type_,pythran_supported) or is_pythran_expr(type_)
|
||||
|
||||
|
||||
@cython.ccall
|
||||
def is_pythran_expr(type_):
|
||||
return type_.is_pythran_expr
|
||||
|
||||
|
||||
def is_pythran_buffer(type_):
|
||||
return (type_.is_numpy_buffer and is_pythran_supported_dtype(type_.dtype) and
|
||||
type_.mode in ("c", "strided") and not type_.cast)
|
||||
|
||||
def pythran_get_func_include_file(func):
|
||||
func = np_func_to_list(func)
|
||||
return "pythonic/numpy/%s.hpp" % "/".join(func)
|
||||
|
||||
def include_pythran_generic(env):
|
||||
# Generic files
|
||||
env.add_include_file("pythonic/core.hpp")
|
||||
env.add_include_file("pythonic/python/core.hpp")
|
||||
env.add_include_file("pythonic/types/bool.hpp")
|
||||
env.add_include_file("pythonic/types/ndarray.hpp")
|
||||
env.add_include_file("pythonic/numpy/power.hpp")
|
||||
env.add_include_file("pythonic/%s/slice.hpp" % pythran_builtins)
|
||||
env.add_include_file("<new>") # for placement new
|
||||
|
||||
for i in (8, 16, 32, 64):
|
||||
env.add_include_file("pythonic/types/uint%d.hpp" % i)
|
||||
env.add_include_file("pythonic/types/int%d.hpp" % i)
|
||||
for t in ("float", "float32", "float64", "set", "slice", "tuple", "int",
|
||||
"complex", "complex64", "complex128"):
|
||||
env.add_include_file("pythonic/types/%s.hpp" % t)
|
||||
@@ -0,0 +1,67 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import cython
|
||||
|
||||
from ..Plex.Scanners cimport Scanner
|
||||
|
||||
cdef unicode any_string_prefix, IDENT
|
||||
|
||||
cdef get_lexicon()
|
||||
cdef initial_compile_time_env()
|
||||
|
||||
cdef class Method:
|
||||
cdef object name
|
||||
cdef dict kwargs
|
||||
cdef readonly object __name__ # for tracing the scanner
|
||||
|
||||
## methods commented with '##' out are used by Parsing.py when compiled.
|
||||
|
||||
@cython.final
|
||||
cdef class CompileTimeScope:
|
||||
cdef public dict entries
|
||||
cdef public CompileTimeScope outer
|
||||
##cdef declare(self, name, value)
|
||||
##cdef lookup_here(self, name)
|
||||
##cpdef lookup(self, name)
|
||||
|
||||
@cython.final
|
||||
cdef class PyrexScanner(Scanner):
|
||||
cdef public context
|
||||
cdef public list included_files
|
||||
cdef public CompileTimeScope compile_time_env
|
||||
cdef public bint compile_time_eval
|
||||
cdef public bint compile_time_expr
|
||||
cdef public bint parse_comments
|
||||
cdef public bint in_python_file
|
||||
cdef public source_encoding
|
||||
cdef set keywords
|
||||
cdef public list indentation_stack
|
||||
cdef public indentation_char
|
||||
cdef public int bracket_nesting_level
|
||||
cdef readonly bint async_enabled
|
||||
cdef public sy
|
||||
cdef public systring
|
||||
|
||||
cdef long current_level(self)
|
||||
#cpdef commentline(self, text)
|
||||
#cpdef open_bracket_action(self, text)
|
||||
#cpdef close_bracket_action(self, text)
|
||||
#cpdef newline_action(self, text)
|
||||
#cpdef begin_string_action(self, text)
|
||||
#cpdef end_string_action(self, text)
|
||||
#cpdef unclosed_string_action(self, text)
|
||||
@cython.locals(current_level=cython.long, new_level=cython.long)
|
||||
cpdef indentation_action(self, text)
|
||||
#cpdef eof_action(self, text)
|
||||
##cdef next(self)
|
||||
##cdef peek(self)
|
||||
#cpdef put_back(self, sy, systring)
|
||||
#cdef unread(self, token, value)
|
||||
##cdef bint expect(self, what, message = *) except -2
|
||||
##cdef expect_keyword(self, what, message = *)
|
||||
##cdef expected(self, what, message = *)
|
||||
##cdef expect_indent(self)
|
||||
##cdef expect_dedent(self)
|
||||
##cdef expect_newline(self, message=*, bint ignore_semicolon=*)
|
||||
##cdef int enter_async(self) except -1
|
||||
##cdef int exit_async(self) except -1
|
||||
@@ -0,0 +1,551 @@
|
||||
# cython: infer_types=True, language_level=3, py2_import=True, auto_pickle=False
|
||||
#
|
||||
# Cython Scanner
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import cython
|
||||
cython.declare(make_lexicon=object, lexicon=object,
|
||||
print_function=object, error=object, warning=object,
|
||||
os=object, platform=object)
|
||||
|
||||
import os
|
||||
import platform
|
||||
|
||||
from .. import Utils
|
||||
from ..Plex.Scanners import Scanner
|
||||
from ..Plex.Errors import UnrecognizedInput
|
||||
from .Errors import error, warning
|
||||
from .Lexicon import any_string_prefix, make_lexicon, IDENT
|
||||
from .Future import print_function
|
||||
|
||||
debug_scanner = 0
|
||||
trace_scanner = 0
|
||||
scanner_debug_flags = 0
|
||||
scanner_dump_file = None
|
||||
|
||||
lexicon = None
|
||||
|
||||
|
||||
def get_lexicon():
|
||||
global lexicon
|
||||
if not lexicon:
|
||||
lexicon = make_lexicon()
|
||||
return lexicon
|
||||
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
py_reserved_words = [
|
||||
"global", "nonlocal", "def", "class", "print", "del", "pass", "break",
|
||||
"continue", "return", "raise", "import", "exec", "try",
|
||||
"except", "finally", "while", "if", "elif", "else", "for",
|
||||
"in", "assert", "and", "or", "not", "is", "lambda",
|
||||
"from", "yield", "with",
|
||||
]
|
||||
|
||||
pyx_reserved_words = py_reserved_words + [
|
||||
"include", "ctypedef", "cdef", "cpdef",
|
||||
"cimport", "DEF", "IF", "ELIF", "ELSE"
|
||||
]
|
||||
|
||||
|
||||
class Method(object):
|
||||
|
||||
def __init__(self, name, **kwargs):
|
||||
self.name = name
|
||||
self.kwargs = kwargs or None
|
||||
self.__name__ = name # for Plex tracing
|
||||
|
||||
def __call__(self, stream, text):
|
||||
method = getattr(stream, self.name)
|
||||
# self.kwargs is almost always unused => avoid call overhead
|
||||
return method(text, **self.kwargs) if self.kwargs is not None else method(text)
|
||||
|
||||
def __copy__(self):
|
||||
return self # immutable, no need to copy
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self # immutable, no need to copy
|
||||
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
class CompileTimeScope(object):
|
||||
|
||||
def __init__(self, outer=None):
|
||||
self.entries = {}
|
||||
self.outer = outer
|
||||
|
||||
def declare(self, name, value):
|
||||
self.entries[name] = value
|
||||
|
||||
def update(self, other):
|
||||
self.entries.update(other)
|
||||
|
||||
def lookup_here(self, name):
|
||||
return self.entries[name]
|
||||
|
||||
def __contains__(self, name):
|
||||
return name in self.entries
|
||||
|
||||
def lookup(self, name):
|
||||
try:
|
||||
return self.lookup_here(name)
|
||||
except KeyError:
|
||||
outer = self.outer
|
||||
if outer:
|
||||
return outer.lookup(name)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def initial_compile_time_env():
|
||||
benv = CompileTimeScope()
|
||||
names = ('UNAME_SYSNAME', 'UNAME_NODENAME', 'UNAME_RELEASE', 'UNAME_VERSION', 'UNAME_MACHINE')
|
||||
for name, value in zip(names, platform.uname()):
|
||||
benv.declare(name, value)
|
||||
try:
|
||||
import __builtin__ as builtins
|
||||
except ImportError:
|
||||
import builtins
|
||||
|
||||
names = (
|
||||
'False', 'True',
|
||||
'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes',
|
||||
'chr', 'cmp', 'complex', 'dict', 'divmod', 'enumerate', 'filter',
|
||||
'float', 'format', 'frozenset', 'hash', 'hex', 'int', 'len',
|
||||
'list', 'map', 'max', 'min', 'oct', 'ord', 'pow', 'range',
|
||||
'repr', 'reversed', 'round', 'set', 'slice', 'sorted', 'str',
|
||||
'sum', 'tuple', 'zip',
|
||||
### defined below in a platform independent way
|
||||
# 'long', 'unicode', 'reduce', 'xrange'
|
||||
)
|
||||
|
||||
for name in names:
|
||||
try:
|
||||
benv.declare(name, getattr(builtins, name))
|
||||
except AttributeError:
|
||||
# ignore, likely Py3
|
||||
pass
|
||||
|
||||
# Py2/3 adaptations
|
||||
from functools import reduce
|
||||
benv.declare('reduce', reduce)
|
||||
benv.declare('unicode', getattr(builtins, 'unicode', getattr(builtins, 'str')))
|
||||
benv.declare('long', getattr(builtins, 'long', getattr(builtins, 'int')))
|
||||
benv.declare('xrange', getattr(builtins, 'xrange', getattr(builtins, 'range')))
|
||||
|
||||
denv = CompileTimeScope(benv)
|
||||
return denv
|
||||
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
class SourceDescriptor(object):
|
||||
"""
|
||||
A SourceDescriptor should be considered immutable.
|
||||
"""
|
||||
filename = None
|
||||
|
||||
_file_type = 'pyx'
|
||||
|
||||
_escaped_description = None
|
||||
_cmp_name = ''
|
||||
def __str__(self):
|
||||
assert False # To catch all places where a descriptor is used directly as a filename
|
||||
|
||||
def set_file_type_from_name(self, filename):
|
||||
name, ext = os.path.splitext(filename)
|
||||
self._file_type = ext in ('.pyx', '.pxd', '.py') and ext[1:] or 'pyx'
|
||||
|
||||
def is_cython_file(self):
|
||||
return self._file_type in ('pyx', 'pxd')
|
||||
|
||||
def is_python_file(self):
|
||||
return self._file_type == 'py'
|
||||
|
||||
def get_escaped_description(self):
|
||||
if self._escaped_description is None:
|
||||
esc_desc = \
|
||||
self.get_description().encode('ASCII', 'replace').decode("ASCII")
|
||||
# Use forward slashes on Windows since these paths
|
||||
# will be used in the #line directives in the C/C++ files.
|
||||
self._escaped_description = esc_desc.replace('\\', '/')
|
||||
return self._escaped_description
|
||||
|
||||
def __gt__(self, other):
|
||||
# this is only used to provide some sort of order
|
||||
try:
|
||||
return self._cmp_name > other._cmp_name
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
# this is only used to provide some sort of order
|
||||
try:
|
||||
return self._cmp_name < other._cmp_name
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
# this is only used to provide some sort of order
|
||||
try:
|
||||
return self._cmp_name <= other._cmp_name
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def __copy__(self):
|
||||
return self # immutable, no need to copy
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self # immutable, no need to copy
|
||||
|
||||
|
||||
class FileSourceDescriptor(SourceDescriptor):
|
||||
"""
|
||||
Represents a code source. A code source is a more generic abstraction
|
||||
for a "filename" (as sometimes the code doesn't come from a file).
|
||||
Instances of code sources are passed to Scanner.__init__ as the
|
||||
optional name argument and will be passed back when asking for
|
||||
the position()-tuple.
|
||||
"""
|
||||
def __init__(self, filename, path_description=None):
|
||||
filename = Utils.decode_filename(filename)
|
||||
self.path_description = path_description or filename
|
||||
self.filename = filename
|
||||
# Prefer relative paths to current directory (which is most likely the project root) over absolute paths.
|
||||
workdir = os.path.abspath('.') + os.sep
|
||||
self.file_path = filename[len(workdir):] if filename.startswith(workdir) else filename
|
||||
self.set_file_type_from_name(filename)
|
||||
self._cmp_name = filename
|
||||
self._lines = {}
|
||||
|
||||
def get_lines(self, encoding=None, error_handling=None):
|
||||
# we cache the lines only the second time this is called, in
|
||||
# order to save memory when they are only used once
|
||||
key = (encoding, error_handling)
|
||||
try:
|
||||
lines = self._lines[key]
|
||||
if lines is not None:
|
||||
return lines
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
with Utils.open_source_file(self.filename, encoding=encoding, error_handling=error_handling) as f:
|
||||
lines = list(f)
|
||||
|
||||
if key in self._lines:
|
||||
self._lines[key] = lines
|
||||
else:
|
||||
# do not cache the first access, but remember that we
|
||||
# already read it once
|
||||
self._lines[key] = None
|
||||
return lines
|
||||
|
||||
def get_description(self):
|
||||
try:
|
||||
return os.path.relpath(self.path_description)
|
||||
except ValueError:
|
||||
# path not under current directory => use complete file path
|
||||
return self.path_description
|
||||
|
||||
def get_error_description(self):
|
||||
path = self.filename
|
||||
cwd = Utils.decode_filename(os.getcwd() + os.path.sep)
|
||||
if path.startswith(cwd):
|
||||
return path[len(cwd):]
|
||||
return path
|
||||
|
||||
def get_filenametable_entry(self):
|
||||
return self.file_path
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, FileSourceDescriptor) and self.filename == other.filename
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.filename)
|
||||
|
||||
def __repr__(self):
|
||||
return "<FileSourceDescriptor:%s>" % self.filename
|
||||
|
||||
|
||||
class StringSourceDescriptor(SourceDescriptor):
|
||||
"""
|
||||
Instances of this class can be used instead of a filenames if the
|
||||
code originates from a string object.
|
||||
"""
|
||||
def __init__(self, name, code):
|
||||
self.name = name
|
||||
#self.set_file_type_from_name(name)
|
||||
self.codelines = [x + "\n" for x in code.split("\n")]
|
||||
self._cmp_name = name
|
||||
|
||||
def get_lines(self, encoding=None, error_handling=None):
|
||||
if not encoding:
|
||||
return self.codelines
|
||||
else:
|
||||
return [line.encode(encoding, error_handling).decode(encoding)
|
||||
for line in self.codelines]
|
||||
|
||||
def get_description(self):
|
||||
return self.name
|
||||
|
||||
get_error_description = get_description
|
||||
|
||||
def get_filenametable_entry(self):
|
||||
return "stringsource"
|
||||
|
||||
def __hash__(self):
|
||||
return id(self)
|
||||
# Do not hash on the name, an identical string source should be the
|
||||
# same object (name is often defaulted in other places)
|
||||
# return hash(self.name)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, StringSourceDescriptor) and self.name == other.name
|
||||
|
||||
def __repr__(self):
|
||||
return "<StringSourceDescriptor:%s>" % self.name
|
||||
|
||||
|
||||
#------------------------------------------------------------------
|
||||
|
||||
class PyrexScanner(Scanner):
|
||||
# context Context Compilation context
|
||||
# included_files [string] Files included with 'include' statement
|
||||
# compile_time_env dict Environment for conditional compilation
|
||||
# compile_time_eval boolean In a true conditional compilation context
|
||||
# compile_time_expr boolean In a compile-time expression context
|
||||
|
||||
def __init__(self, file, filename, parent_scanner=None,
|
||||
scope=None, context=None, source_encoding=None, parse_comments=True, initial_pos=None):
|
||||
Scanner.__init__(self, get_lexicon(), file, filename, initial_pos)
|
||||
|
||||
if filename.is_python_file():
|
||||
self.in_python_file = True
|
||||
self.keywords = set(py_reserved_words)
|
||||
else:
|
||||
self.in_python_file = False
|
||||
self.keywords = set(pyx_reserved_words)
|
||||
|
||||
self.async_enabled = 0
|
||||
|
||||
if parent_scanner:
|
||||
self.context = parent_scanner.context
|
||||
self.included_files = parent_scanner.included_files
|
||||
self.compile_time_env = parent_scanner.compile_time_env
|
||||
self.compile_time_eval = parent_scanner.compile_time_eval
|
||||
self.compile_time_expr = parent_scanner.compile_time_expr
|
||||
|
||||
if parent_scanner.async_enabled:
|
||||
self.enter_async()
|
||||
else:
|
||||
self.context = context
|
||||
self.included_files = scope.included_files
|
||||
self.compile_time_env = initial_compile_time_env()
|
||||
self.compile_time_eval = 1
|
||||
self.compile_time_expr = 0
|
||||
if getattr(context.options, 'compile_time_env', None):
|
||||
self.compile_time_env.update(context.options.compile_time_env)
|
||||
self.parse_comments = parse_comments
|
||||
self.source_encoding = source_encoding
|
||||
self.trace = trace_scanner
|
||||
self.indentation_stack = [0]
|
||||
self.indentation_char = None
|
||||
self.bracket_nesting_level = 0
|
||||
|
||||
self.begin('INDENT')
|
||||
self.sy = ''
|
||||
self.next()
|
||||
|
||||
def commentline(self, text):
|
||||
if self.parse_comments:
|
||||
self.produce('commentline', text)
|
||||
|
||||
def strip_underscores(self, text, symbol):
|
||||
self.produce(symbol, text.replace('_', ''))
|
||||
|
||||
def current_level(self):
|
||||
return self.indentation_stack[-1]
|
||||
|
||||
def open_bracket_action(self, text):
|
||||
self.bracket_nesting_level += 1
|
||||
return text
|
||||
|
||||
def close_bracket_action(self, text):
|
||||
self.bracket_nesting_level -= 1
|
||||
return text
|
||||
|
||||
def newline_action(self, text):
|
||||
if self.bracket_nesting_level == 0:
|
||||
self.begin('INDENT')
|
||||
self.produce('NEWLINE', '')
|
||||
|
||||
string_states = {
|
||||
"'": 'SQ_STRING',
|
||||
'"': 'DQ_STRING',
|
||||
"'''": 'TSQ_STRING',
|
||||
'"""': 'TDQ_STRING'
|
||||
}
|
||||
|
||||
def begin_string_action(self, text):
|
||||
while text[:1] in any_string_prefix:
|
||||
text = text[1:]
|
||||
self.begin(self.string_states[text])
|
||||
self.produce('BEGIN_STRING')
|
||||
|
||||
def end_string_action(self, text):
|
||||
self.begin('')
|
||||
self.produce('END_STRING')
|
||||
|
||||
def unclosed_string_action(self, text):
|
||||
self.end_string_action(text)
|
||||
self.error("Unclosed string literal")
|
||||
|
||||
def indentation_action(self, text):
|
||||
self.begin('')
|
||||
# Indentation within brackets should be ignored.
|
||||
#if self.bracket_nesting_level > 0:
|
||||
# return
|
||||
# Check that tabs and spaces are being used consistently.
|
||||
if text:
|
||||
c = text[0]
|
||||
#print "Scanner.indentation_action: indent with", repr(c) ###
|
||||
if self.indentation_char is None:
|
||||
self.indentation_char = c
|
||||
#print "Scanner.indentation_action: setting indent_char to", repr(c)
|
||||
else:
|
||||
if self.indentation_char != c:
|
||||
self.error("Mixed use of tabs and spaces")
|
||||
if text.replace(c, "") != "":
|
||||
self.error("Mixed use of tabs and spaces")
|
||||
# Figure out how many indents/dedents to do
|
||||
current_level = self.current_level()
|
||||
new_level = len(text)
|
||||
#print "Changing indent level from", current_level, "to", new_level ###
|
||||
if new_level == current_level:
|
||||
return
|
||||
elif new_level > current_level:
|
||||
#print "...pushing level", new_level ###
|
||||
self.indentation_stack.append(new_level)
|
||||
self.produce('INDENT', '')
|
||||
else:
|
||||
while new_level < self.current_level():
|
||||
#print "...popping level", self.indentation_stack[-1] ###
|
||||
self.indentation_stack.pop()
|
||||
self.produce('DEDENT', '')
|
||||
#print "...current level now", self.current_level() ###
|
||||
if new_level != self.current_level():
|
||||
self.error("Inconsistent indentation")
|
||||
|
||||
def eof_action(self, text):
|
||||
while len(self.indentation_stack) > 1:
|
||||
self.produce('DEDENT', '')
|
||||
self.indentation_stack.pop()
|
||||
self.produce('EOF', '')
|
||||
|
||||
def next(self):
|
||||
try:
|
||||
sy, systring = self.read()
|
||||
except UnrecognizedInput:
|
||||
self.error("Unrecognized character")
|
||||
return # just a marker, error() always raises
|
||||
if sy == IDENT:
|
||||
if systring in self.keywords:
|
||||
if systring == u'print' and print_function in self.context.future_directives:
|
||||
self.keywords.discard('print')
|
||||
elif systring == u'exec' and self.context.language_level >= 3:
|
||||
self.keywords.discard('exec')
|
||||
else:
|
||||
sy = systring
|
||||
systring = self.context.intern_ustring(systring)
|
||||
self.sy = sy
|
||||
self.systring = systring
|
||||
if False: # debug_scanner:
|
||||
_, line, col = self.position()
|
||||
if not self.systring or self.sy == self.systring:
|
||||
t = self.sy
|
||||
else:
|
||||
t = "%s %s" % (self.sy, self.systring)
|
||||
print("--- %3d %2d %s" % (line, col, t))
|
||||
|
||||
def peek(self):
|
||||
saved = self.sy, self.systring
|
||||
self.next()
|
||||
next = self.sy, self.systring
|
||||
self.unread(*next)
|
||||
self.sy, self.systring = saved
|
||||
return next
|
||||
|
||||
def put_back(self, sy, systring):
|
||||
self.unread(self.sy, self.systring)
|
||||
self.sy = sy
|
||||
self.systring = systring
|
||||
|
||||
def unread(self, token, value):
|
||||
# This method should be added to Plex
|
||||
self.queue.insert(0, (token, value))
|
||||
|
||||
def error(self, message, pos=None, fatal=True):
|
||||
if pos is None:
|
||||
pos = self.position()
|
||||
if self.sy == 'INDENT':
|
||||
error(pos, "Possible inconsistent indentation")
|
||||
err = error(pos, message)
|
||||
if fatal: raise err
|
||||
|
||||
def expect(self, what, message=None):
|
||||
if self.sy == what:
|
||||
self.next()
|
||||
else:
|
||||
self.expected(what, message)
|
||||
|
||||
def expect_keyword(self, what, message=None):
|
||||
if self.sy == IDENT and self.systring == what:
|
||||
self.next()
|
||||
else:
|
||||
self.expected(what, message)
|
||||
|
||||
def expected(self, what, message=None):
|
||||
if message:
|
||||
self.error(message)
|
||||
else:
|
||||
if self.sy == IDENT:
|
||||
found = self.systring
|
||||
else:
|
||||
found = self.sy
|
||||
self.error("Expected '%s', found '%s'" % (what, found))
|
||||
|
||||
def expect_indent(self):
|
||||
self.expect('INDENT', "Expected an increase in indentation level")
|
||||
|
||||
def expect_dedent(self):
|
||||
self.expect('DEDENT', "Expected a decrease in indentation level")
|
||||
|
||||
def expect_newline(self, message="Expected a newline", ignore_semicolon=False):
|
||||
# Expect either a newline or end of file
|
||||
useless_trailing_semicolon = None
|
||||
if ignore_semicolon and self.sy == ';':
|
||||
useless_trailing_semicolon = self.position()
|
||||
self.next()
|
||||
if self.sy != 'EOF':
|
||||
self.expect('NEWLINE', message)
|
||||
if useless_trailing_semicolon is not None:
|
||||
warning(useless_trailing_semicolon, "useless trailing semicolon")
|
||||
|
||||
def enter_async(self):
|
||||
self.async_enabled += 1
|
||||
if self.async_enabled == 1:
|
||||
self.keywords.add('async')
|
||||
self.keywords.add('await')
|
||||
|
||||
def exit_async(self):
|
||||
assert self.async_enabled > 0
|
||||
self.async_enabled -= 1
|
||||
if not self.async_enabled:
|
||||
self.keywords.discard('await')
|
||||
self.keywords.discard('async')
|
||||
if self.sy in ('async', 'await'):
|
||||
self.sy, self.systring = IDENT, self.context.intern_ustring(self.sy)
|
||||
@@ -0,0 +1,363 @@
|
||||
#
|
||||
# Cython -- encoding related tools
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
_unicode, _str, _bytes, _unichr = str, str, bytes, chr
|
||||
IS_PYTHON3 = True
|
||||
else:
|
||||
_unicode, _str, _bytes, _unichr = unicode, str, str, unichr
|
||||
IS_PYTHON3 = False
|
||||
|
||||
empty_bytes = _bytes()
|
||||
empty_unicode = _unicode()
|
||||
|
||||
join_bytes = empty_bytes.join
|
||||
|
||||
|
||||
class UnicodeLiteralBuilder(object):
|
||||
"""Assemble a unicode string.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.chars = []
|
||||
|
||||
def append(self, characters):
|
||||
if isinstance(characters, _bytes):
|
||||
# this came from a Py2 string literal in the parser code
|
||||
characters = characters.decode("ASCII")
|
||||
assert isinstance(characters, _unicode), str(type(characters))
|
||||
self.chars.append(characters)
|
||||
|
||||
if sys.maxunicode == 65535:
|
||||
def append_charval(self, char_number):
|
||||
if char_number > 65535:
|
||||
# wide Unicode character on narrow platform => replace
|
||||
# by surrogate pair
|
||||
char_number -= 0x10000
|
||||
self.chars.append( _unichr((char_number // 1024) + 0xD800) )
|
||||
self.chars.append( _unichr((char_number % 1024) + 0xDC00) )
|
||||
else:
|
||||
self.chars.append( _unichr(char_number) )
|
||||
else:
|
||||
def append_charval(self, char_number):
|
||||
self.chars.append( _unichr(char_number) )
|
||||
|
||||
def append_uescape(self, char_number, escape_string):
|
||||
self.append_charval(char_number)
|
||||
|
||||
def getstring(self):
|
||||
return EncodedString(u''.join(self.chars))
|
||||
|
||||
def getstrings(self):
|
||||
return (None, self.getstring())
|
||||
|
||||
|
||||
class BytesLiteralBuilder(object):
|
||||
"""Assemble a byte string or char value.
|
||||
"""
|
||||
def __init__(self, target_encoding):
|
||||
self.chars = []
|
||||
self.target_encoding = target_encoding
|
||||
|
||||
def append(self, characters):
|
||||
if isinstance(characters, _unicode):
|
||||
characters = characters.encode(self.target_encoding)
|
||||
assert isinstance(characters, _bytes), str(type(characters))
|
||||
self.chars.append(characters)
|
||||
|
||||
def append_charval(self, char_number):
|
||||
self.chars.append( _unichr(char_number).encode('ISO-8859-1') )
|
||||
|
||||
def append_uescape(self, char_number, escape_string):
|
||||
self.append(escape_string)
|
||||
|
||||
def getstring(self):
|
||||
# this *must* return a byte string!
|
||||
return bytes_literal(join_bytes(self.chars), self.target_encoding)
|
||||
|
||||
def getchar(self):
|
||||
# this *must* return a byte string!
|
||||
return self.getstring()
|
||||
|
||||
def getstrings(self):
|
||||
return (self.getstring(), None)
|
||||
|
||||
|
||||
class StrLiteralBuilder(object):
|
||||
"""Assemble both a bytes and a unicode representation of a string.
|
||||
"""
|
||||
def __init__(self, target_encoding):
|
||||
self._bytes = BytesLiteralBuilder(target_encoding)
|
||||
self._unicode = UnicodeLiteralBuilder()
|
||||
|
||||
def append(self, characters):
|
||||
self._bytes.append(characters)
|
||||
self._unicode.append(characters)
|
||||
|
||||
def append_charval(self, char_number):
|
||||
self._bytes.append_charval(char_number)
|
||||
self._unicode.append_charval(char_number)
|
||||
|
||||
def append_uescape(self, char_number, escape_string):
|
||||
self._bytes.append(escape_string)
|
||||
self._unicode.append_charval(char_number)
|
||||
|
||||
def getstrings(self):
|
||||
return (self._bytes.getstring(), self._unicode.getstring())
|
||||
|
||||
|
||||
class EncodedString(_unicode):
|
||||
# unicode string subclass to keep track of the original encoding.
|
||||
# 'encoding' is None for unicode strings and the source encoding
|
||||
# otherwise
|
||||
encoding = None
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self
|
||||
|
||||
def byteencode(self):
|
||||
assert self.encoding is not None
|
||||
return self.encode(self.encoding)
|
||||
|
||||
def utf8encode(self):
|
||||
assert self.encoding is None
|
||||
return self.encode("UTF-8")
|
||||
|
||||
@property
|
||||
def is_unicode(self):
|
||||
return self.encoding is None
|
||||
|
||||
def contains_surrogates(self):
|
||||
return string_contains_surrogates(self)
|
||||
|
||||
def as_utf8_string(self):
|
||||
return bytes_literal(self.utf8encode(), 'utf8')
|
||||
|
||||
|
||||
def string_contains_surrogates(ustring):
|
||||
"""
|
||||
Check if the unicode string contains surrogate code points
|
||||
on a CPython platform with wide (UCS-4) or narrow (UTF-16)
|
||||
Unicode, i.e. characters that would be spelled as two
|
||||
separate code units on a narrow platform.
|
||||
"""
|
||||
for c in map(ord, ustring):
|
||||
if c > 65535: # can only happen on wide platforms
|
||||
return True
|
||||
if 0xD800 <= c <= 0xDFFF:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def string_contains_lone_surrogates(ustring):
|
||||
"""
|
||||
Check if the unicode string contains lone surrogate code points
|
||||
on a CPython platform with wide (UCS-4) or narrow (UTF-16)
|
||||
Unicode, i.e. characters that would be spelled as two
|
||||
separate code units on a narrow platform, but that do not form a pair.
|
||||
"""
|
||||
last_was_start = False
|
||||
unicode_uses_surrogate_encoding = sys.maxunicode == 65535
|
||||
for c in map(ord, ustring):
|
||||
# surrogates tend to be rare
|
||||
if c < 0xD800 or c > 0xDFFF:
|
||||
if last_was_start:
|
||||
return True
|
||||
elif not unicode_uses_surrogate_encoding:
|
||||
# on 32bit Unicode platforms, there is never a pair
|
||||
return True
|
||||
elif c <= 0xDBFF:
|
||||
if last_was_start:
|
||||
return True # lone start
|
||||
last_was_start = True
|
||||
else:
|
||||
if not last_was_start:
|
||||
return True # lone end
|
||||
last_was_start = False
|
||||
return last_was_start
|
||||
|
||||
|
||||
class BytesLiteral(_bytes):
|
||||
# bytes subclass that is compatible with EncodedString
|
||||
encoding = None
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
return self
|
||||
|
||||
def byteencode(self):
|
||||
if IS_PYTHON3:
|
||||
return _bytes(self)
|
||||
else:
|
||||
# fake-recode the string to make it a plain bytes object
|
||||
return self.decode('ISO-8859-1').encode('ISO-8859-1')
|
||||
|
||||
def utf8encode(self):
|
||||
assert False, "this is not a unicode string: %r" % self
|
||||
|
||||
def __str__(self):
|
||||
"""Fake-decode the byte string to unicode to support %
|
||||
formatting of unicode strings.
|
||||
"""
|
||||
return self.decode('ISO-8859-1')
|
||||
|
||||
is_unicode = False
|
||||
|
||||
def as_c_string_literal(self):
|
||||
value = split_string_literal(escape_byte_string(self))
|
||||
return '"%s"' % value
|
||||
|
||||
|
||||
def bytes_literal(s, encoding):
|
||||
assert isinstance(s, bytes)
|
||||
s = BytesLiteral(s)
|
||||
s.encoding = encoding
|
||||
return s
|
||||
|
||||
|
||||
def encoded_string(s, encoding):
|
||||
assert isinstance(s, (_unicode, bytes))
|
||||
s = EncodedString(s)
|
||||
if encoding is not None:
|
||||
s.encoding = encoding
|
||||
return s
|
||||
|
||||
|
||||
char_from_escape_sequence = {
|
||||
r'\a' : u'\a',
|
||||
r'\b' : u'\b',
|
||||
r'\f' : u'\f',
|
||||
r'\n' : u'\n',
|
||||
r'\r' : u'\r',
|
||||
r'\t' : u'\t',
|
||||
r'\v' : u'\v',
|
||||
}.get
|
||||
|
||||
_c_special = ('\\', '??', '"') + tuple(map(chr, range(32)))
|
||||
|
||||
|
||||
def _to_escape_sequence(s):
|
||||
if s in '\n\r\t':
|
||||
return repr(s)[1:-1]
|
||||
elif s == '"':
|
||||
return r'\"'
|
||||
elif s == '\\':
|
||||
return r'\\'
|
||||
else:
|
||||
# within a character sequence, oct passes much better than hex
|
||||
return ''.join(['\\%03o' % ord(c) for c in s])
|
||||
|
||||
|
||||
def _build_specials_replacer():
|
||||
subexps = []
|
||||
replacements = {}
|
||||
for special in _c_special:
|
||||
regexp = ''.join(['[%s]' % c.replace('\\', '\\\\') for c in special])
|
||||
subexps.append(regexp)
|
||||
replacements[special.encode('ASCII')] = _to_escape_sequence(special).encode('ASCII')
|
||||
sub = re.compile(('(%s)' % '|'.join(subexps)).encode('ASCII')).sub
|
||||
def replace_specials(m):
|
||||
return replacements[m.group(1)]
|
||||
def replace(s):
|
||||
return sub(replace_specials, s)
|
||||
return replace
|
||||
|
||||
_replace_specials = _build_specials_replacer()
|
||||
|
||||
|
||||
def escape_char(c):
|
||||
if IS_PYTHON3:
|
||||
c = c.decode('ISO-8859-1')
|
||||
if c in '\n\r\t\\':
|
||||
return repr(c)[1:-1]
|
||||
elif c == "'":
|
||||
return "\\'"
|
||||
n = ord(c)
|
||||
if n < 32 or n > 127:
|
||||
# hex works well for characters
|
||||
return "\\x%02X" % n
|
||||
else:
|
||||
return c
|
||||
|
||||
def escape_byte_string(s):
|
||||
"""Escape a byte string so that it can be written into C code.
|
||||
Note that this returns a Unicode string instead which, when
|
||||
encoded as ISO-8859-1, will result in the correct byte sequence
|
||||
being written.
|
||||
"""
|
||||
s = _replace_specials(s)
|
||||
try:
|
||||
return s.decode("ASCII") # trial decoding: plain ASCII => done
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
if IS_PYTHON3:
|
||||
s_new = bytearray()
|
||||
append, extend = s_new.append, s_new.extend
|
||||
for b in s:
|
||||
if b >= 128:
|
||||
extend(('\\%3o' % b).encode('ASCII'))
|
||||
else:
|
||||
append(b)
|
||||
return s_new.decode('ISO-8859-1')
|
||||
else:
|
||||
l = []
|
||||
append = l.append
|
||||
for c in s:
|
||||
o = ord(c)
|
||||
if o >= 128:
|
||||
append('\\%3o' % o)
|
||||
else:
|
||||
append(c)
|
||||
return join_bytes(l).decode('ISO-8859-1')
|
||||
|
||||
def split_string_literal(s, limit=2000):
|
||||
# MSVC can't handle long string literals.
|
||||
if len(s) < limit:
|
||||
return s
|
||||
else:
|
||||
start = 0
|
||||
chunks = []
|
||||
while start < len(s):
|
||||
end = start + limit
|
||||
if len(s) > end-4 and '\\' in s[end-4:end]:
|
||||
end -= 4 - s[end-4:end].find('\\') # just before the backslash
|
||||
while s[end-1] == '\\':
|
||||
end -= 1
|
||||
if end == start:
|
||||
# must have been a long line of backslashes
|
||||
end = start + limit - (limit % 2) - 4
|
||||
break
|
||||
chunks.append(s[start:end])
|
||||
start = end
|
||||
return '""'.join(chunks)
|
||||
|
||||
def encode_pyunicode_string(s):
|
||||
"""Create Py_UNICODE[] representation of a given unicode string.
|
||||
"""
|
||||
s = list(map(ord, s)) + [0]
|
||||
|
||||
if sys.maxunicode >= 0x10000: # Wide build or Py3.3
|
||||
utf16, utf32 = [], s
|
||||
for code_point in s:
|
||||
if code_point >= 0x10000: # outside of BMP
|
||||
high, low = divmod(code_point - 0x10000, 1024)
|
||||
utf16.append(high + 0xD800)
|
||||
utf16.append(low + 0xDC00)
|
||||
else:
|
||||
utf16.append(code_point)
|
||||
else:
|
||||
utf16, utf32 = s, []
|
||||
for code_unit in s:
|
||||
if 0xDC00 <= code_unit <= 0xDFFF and utf32 and 0xD800 <= utf32[-1] <= 0xDBFF:
|
||||
high, low = utf32[-1], code_unit
|
||||
utf32[-1] = ((high & 0x3FF) << 10) + (low & 0x3FF) + 0x10000
|
||||
else:
|
||||
utf32.append(code_unit)
|
||||
|
||||
if utf16 == utf32:
|
||||
utf16 = []
|
||||
return ",".join(map(_unicode, utf16)), ",".join(map(_unicode, utf32))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
from Cython.TestUtils import CythonTest
|
||||
import Cython.Compiler.Errors as Errors
|
||||
from Cython.Compiler.Nodes import *
|
||||
from Cython.Compiler.ParseTreeTransforms import *
|
||||
from Cython.Compiler.Buffer import *
|
||||
|
||||
|
||||
class TestBufferParsing(CythonTest):
|
||||
# First, we only test the raw parser, i.e.
|
||||
# the number and contents of arguments are NOT checked.
|
||||
# However "dtype"/the first positional argument is special-cased
|
||||
# to parse a type argument rather than an expression
|
||||
|
||||
def parse(self, s):
|
||||
return self.should_not_fail(lambda: self.fragment(s)).root
|
||||
|
||||
def not_parseable(self, expected_error, s):
|
||||
e = self.should_fail(lambda: self.fragment(s), Errors.CompileError)
|
||||
self.assertEqual(expected_error, e.message_only)
|
||||
|
||||
def test_basic(self):
|
||||
t = self.parse(u"cdef object[float, 4, ndim=2, foo=foo] x")
|
||||
bufnode = t.stats[0].base_type
|
||||
self.assertTrue(isinstance(bufnode, TemplatedTypeNode))
|
||||
self.assertEqual(2, len(bufnode.positional_args))
|
||||
# print bufnode.dump()
|
||||
# should put more here...
|
||||
|
||||
def test_type_pos(self):
|
||||
self.parse(u"cdef object[short unsigned int, 3] x")
|
||||
|
||||
def test_type_keyword(self):
|
||||
self.parse(u"cdef object[foo=foo, dtype=short unsigned int] x")
|
||||
|
||||
def test_pos_after_key(self):
|
||||
self.not_parseable("Non-keyword arg following keyword arg",
|
||||
u"cdef object[foo=1, 2] x")
|
||||
|
||||
|
||||
# See also tests/error/e_bufaccess.pyx and tets/run/bufaccess.pyx
|
||||
# THESE TESTS ARE NOW DISABLED, the code they test was pretty much
|
||||
# refactored away
|
||||
class TestBufferOptions(CythonTest):
|
||||
# Tests the full parsing of the options within the brackets
|
||||
|
||||
def nonfatal_error(self, error):
|
||||
# We're passing self as context to transform to trap this
|
||||
self.error = error
|
||||
self.assertTrue(self.expect_error)
|
||||
|
||||
def parse_opts(self, opts, expect_error=False):
|
||||
assert opts != ""
|
||||
s = u"def f():\n cdef object[%s] x" % opts
|
||||
self.expect_error = expect_error
|
||||
root = self.fragment(s, pipeline=[NormalizeTree(self), PostParse(self)]).root
|
||||
if not expect_error:
|
||||
vardef = root.stats[0].body.stats[0]
|
||||
assert isinstance(vardef, CVarDefNode) # use normal assert as this is to validate the test code
|
||||
buftype = vardef.base_type
|
||||
self.assertTrue(isinstance(buftype, TemplatedTypeNode))
|
||||
self.assertTrue(isinstance(buftype.base_type_node, CSimpleBaseTypeNode))
|
||||
self.assertEqual(u"object", buftype.base_type_node.name)
|
||||
return buftype
|
||||
else:
|
||||
self.assertTrue(len(root.stats[0].body.stats) == 0)
|
||||
|
||||
def non_parse(self, expected_err, opts):
|
||||
self.parse_opts(opts, expect_error=True)
|
||||
# e = self.should_fail(lambda: self.parse_opts(opts))
|
||||
self.assertEqual(expected_err, self.error.message_only)
|
||||
|
||||
def __test_basic(self):
|
||||
buf = self.parse_opts(u"unsigned short int, 3")
|
||||
self.assertTrue(isinstance(buf.dtype_node, CSimpleBaseTypeNode))
|
||||
self.assertTrue(buf.dtype_node.signed == 0 and buf.dtype_node.longness == -1)
|
||||
self.assertEqual(3, buf.ndim)
|
||||
|
||||
def __test_dict(self):
|
||||
buf = self.parse_opts(u"ndim=3, dtype=unsigned short int")
|
||||
self.assertTrue(isinstance(buf.dtype_node, CSimpleBaseTypeNode))
|
||||
self.assertTrue(buf.dtype_node.signed == 0 and buf.dtype_node.longness == -1)
|
||||
self.assertEqual(3, buf.ndim)
|
||||
|
||||
def __test_ndim(self):
|
||||
self.parse_opts(u"int, 2")
|
||||
self.non_parse(ERR_BUF_NDIM, u"int, 'a'")
|
||||
self.non_parse(ERR_BUF_NDIM, u"int, -34")
|
||||
|
||||
def __test_use_DEF(self):
|
||||
t = self.fragment(u"""
|
||||
DEF ndim = 3
|
||||
def f():
|
||||
cdef object[int, ndim] x
|
||||
cdef object[ndim=ndim, dtype=int] y
|
||||
""", pipeline=[NormalizeTree(self), PostParse(self)]).root
|
||||
stats = t.stats[0].body.stats
|
||||
self.assertTrue(stats[0].base_type.ndim == 3)
|
||||
self.assertTrue(stats[1].base_type.ndim == 3)
|
||||
|
||||
# add exotic and impossible combinations as they come along...
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
|
||||
import sys
|
||||
import re
|
||||
from unittest import TestCase
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO # doesn't accept 'str' in Py2
|
||||
|
||||
from .. import Options
|
||||
from ..CmdLine import parse_command_line
|
||||
|
||||
|
||||
def check_global_options(expected_options, white_list=[]):
|
||||
"""
|
||||
returns error message of "" if check Ok
|
||||
"""
|
||||
no_value = object()
|
||||
for name, orig_value in expected_options.items():
|
||||
if name not in white_list:
|
||||
if getattr(Options, name, no_value) != orig_value:
|
||||
return "error in option " + name
|
||||
return ""
|
||||
|
||||
|
||||
class CmdLineParserTest(TestCase):
|
||||
def setUp(self):
|
||||
backup = {}
|
||||
for name, value in vars(Options).items():
|
||||
backup[name] = value
|
||||
self._options_backup = backup
|
||||
|
||||
def tearDown(self):
|
||||
no_value = object()
|
||||
for name, orig_value in self._options_backup.items():
|
||||
if getattr(Options, name, no_value) != orig_value:
|
||||
setattr(Options, name, orig_value)
|
||||
|
||||
def check_default_global_options(self, white_list=[]):
|
||||
self.assertEqual(check_global_options(self._options_backup, white_list), "")
|
||||
|
||||
def check_default_options(self, options, white_list=[]):
|
||||
from ..Main import CompilationOptions, default_options
|
||||
default_options = CompilationOptions(default_options)
|
||||
no_value = object()
|
||||
for name in default_options.__dict__.keys():
|
||||
if name not in white_list:
|
||||
self.assertEqual(getattr(options, name, no_value), getattr(default_options, name), msg="error in option " + name)
|
||||
|
||||
def test_short_options(self):
|
||||
options, sources = parse_command_line([
|
||||
'-V', '-l', '-+', '-t', '-v', '-v', '-v', '-p', '-D', '-a', '-3',
|
||||
])
|
||||
self.assertFalse(sources)
|
||||
self.assertTrue(options.show_version)
|
||||
self.assertTrue(options.use_listing_file)
|
||||
self.assertTrue(options.cplus)
|
||||
self.assertTrue(options.timestamps)
|
||||
self.assertTrue(options.verbose >= 3)
|
||||
self.assertTrue(Options.embed_pos_in_docstring)
|
||||
self.assertFalse(Options.docstrings)
|
||||
self.assertTrue(Options.annotate)
|
||||
self.assertEqual(options.language_level, 3)
|
||||
|
||||
options, sources = parse_command_line([
|
||||
'-f', '-2', 'source.pyx',
|
||||
])
|
||||
self.assertTrue(sources)
|
||||
self.assertTrue(len(sources) == 1)
|
||||
self.assertFalse(options.timestamps)
|
||||
self.assertEqual(options.language_level, 2)
|
||||
|
||||
def test_long_options(self):
|
||||
options, sources = parse_command_line([
|
||||
'--version', '--create-listing', '--cplus', '--embed', '--timestamps',
|
||||
'--verbose', '--verbose', '--verbose',
|
||||
'--embed-positions', '--no-docstrings', '--annotate', '--lenient',
|
||||
])
|
||||
self.assertFalse(sources)
|
||||
self.assertTrue(options.show_version)
|
||||
self.assertTrue(options.use_listing_file)
|
||||
self.assertTrue(options.cplus)
|
||||
self.assertEqual(Options.embed, 'main')
|
||||
self.assertTrue(options.timestamps)
|
||||
self.assertTrue(options.verbose >= 3)
|
||||
self.assertTrue(Options.embed_pos_in_docstring)
|
||||
self.assertFalse(Options.docstrings)
|
||||
self.assertTrue(Options.annotate)
|
||||
self.assertFalse(Options.error_on_unknown_names)
|
||||
self.assertFalse(Options.error_on_uninitialized)
|
||||
|
||||
options, sources = parse_command_line([
|
||||
'--force', 'source.pyx',
|
||||
])
|
||||
self.assertTrue(sources)
|
||||
self.assertTrue(len(sources) == 1)
|
||||
self.assertFalse(options.timestamps)
|
||||
|
||||
def test_options_with_values(self):
|
||||
options, sources = parse_command_line([
|
||||
'--embed=huhu',
|
||||
'-I/test/include/dir1', '--include-dir=/test/include/dir2',
|
||||
'--include-dir', '/test/include/dir3',
|
||||
'--working=/work/dir',
|
||||
'source.pyx',
|
||||
'--output-file=/output/dir',
|
||||
'--pre-import=/pre/import',
|
||||
'--cleanup=3',
|
||||
'--annotate-coverage=cov.xml',
|
||||
'--gdb-outdir=/gdb/outdir',
|
||||
'--directive=wraparound=false',
|
||||
])
|
||||
self.assertEqual(sources, ['source.pyx'])
|
||||
self.assertEqual(Options.embed, 'huhu')
|
||||
self.assertEqual(options.include_path, ['/test/include/dir1', '/test/include/dir2', '/test/include/dir3'])
|
||||
self.assertEqual(options.working_path, '/work/dir')
|
||||
self.assertEqual(options.output_file, '/output/dir')
|
||||
self.assertEqual(Options.pre_import, '/pre/import')
|
||||
self.assertEqual(Options.generate_cleanup_code, 3)
|
||||
self.assertTrue(Options.annotate)
|
||||
self.assertEqual(Options.annotate_coverage_xml, 'cov.xml')
|
||||
self.assertTrue(options.gdb_debug)
|
||||
self.assertEqual(options.output_dir, '/gdb/outdir')
|
||||
|
||||
def test_module_name(self):
|
||||
options, sources = parse_command_line([
|
||||
'source.pyx'
|
||||
])
|
||||
self.assertEqual(options.module_name, None)
|
||||
self.check_default_global_options()
|
||||
self.check_default_options(options)
|
||||
options, sources = parse_command_line([
|
||||
'--module-name', 'foo.bar',
|
||||
'source.pyx'
|
||||
])
|
||||
self.assertEqual(options.module_name, 'foo.bar')
|
||||
self.check_default_global_options()
|
||||
self.check_default_options(options, ['module_name'])
|
||||
|
||||
def test_errors(self):
|
||||
def error(args, regex=None):
|
||||
old_stderr = sys.stderr
|
||||
stderr = sys.stderr = StringIO()
|
||||
try:
|
||||
self.assertRaises(SystemExit, parse_command_line, list(args))
|
||||
finally:
|
||||
sys.stderr = old_stderr
|
||||
msg = stderr.getvalue().strip()
|
||||
self.assertTrue(msg)
|
||||
if regex:
|
||||
self.assertTrue(re.search(regex, msg),
|
||||
'"%s" does not match search "%s"' %
|
||||
(msg, regex))
|
||||
|
||||
error(['-1'],
|
||||
'Unknown compiler flag: -1')
|
||||
error(['-I'])
|
||||
error(['--version=-a'])
|
||||
error(['--version=--annotate=true'])
|
||||
error(['--working'])
|
||||
error(['--verbose=1'])
|
||||
error(['--cleanup'])
|
||||
error(['--debug-disposal-code-wrong-name', 'file3.pyx'],
|
||||
"Unknown debug flag: debug_disposal_code_wrong_name")
|
||||
error(['--module-name', 'foo.pyx'])
|
||||
error(['--module-name', 'foo.bar'])
|
||||
error(['--module-name', 'foo.bar', 'foo.pyx', 'bar.pyx'],
|
||||
"Only one source file allowed when using --module-name")
|
||||
error(['--module-name', 'foo.bar', '--timestamps', 'foo.pyx'],
|
||||
"Cannot use --module-name with --timestamps")
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from copy import deepcopy
|
||||
from unittest import TestCase
|
||||
|
||||
from Cython.Compiler.FlowControl import (
|
||||
NameAssignment, StaticAssignment, Argument, NameDeletion)
|
||||
|
||||
|
||||
class FakeType(object):
|
||||
is_pyobject = True
|
||||
|
||||
|
||||
class FakeNode(object):
|
||||
pos = ('filename.pyx', 1, 2)
|
||||
cf_state = None
|
||||
type = FakeType()
|
||||
|
||||
def infer_type(self, scope):
|
||||
return self.type
|
||||
|
||||
|
||||
class FakeEntry(object):
|
||||
type = FakeType()
|
||||
|
||||
|
||||
class TestGraph(TestCase):
|
||||
def test_deepcopy(self):
|
||||
lhs, rhs = FakeNode(), FakeNode()
|
||||
entry = FakeEntry()
|
||||
entry.pos = lhs.pos
|
||||
|
||||
name_ass = NameAssignment(lhs, rhs, entry)
|
||||
ass = deepcopy(name_ass)
|
||||
self.assertTrue(ass.lhs)
|
||||
self.assertTrue(ass.rhs)
|
||||
self.assertTrue(ass.entry)
|
||||
self.assertEqual(ass.pos, name_ass.pos)
|
||||
self.assertFalse(ass.is_arg)
|
||||
self.assertFalse(ass.is_deletion)
|
||||
|
||||
static_ass = StaticAssignment(entry)
|
||||
ass = deepcopy(static_ass)
|
||||
self.assertTrue(ass.lhs)
|
||||
self.assertTrue(ass.rhs)
|
||||
self.assertTrue(ass.entry)
|
||||
self.assertEqual(ass.pos, static_ass.pos)
|
||||
self.assertFalse(ass.is_arg)
|
||||
self.assertFalse(ass.is_deletion)
|
||||
|
||||
arg_ass = Argument(lhs, rhs, entry)
|
||||
ass = deepcopy(arg_ass)
|
||||
self.assertTrue(ass.lhs)
|
||||
self.assertTrue(ass.rhs)
|
||||
self.assertTrue(ass.entry)
|
||||
self.assertEqual(ass.pos, arg_ass.pos)
|
||||
self.assertTrue(ass.is_arg)
|
||||
self.assertFalse(ass.is_deletion)
|
||||
|
||||
name_del = NameDeletion(lhs, entry)
|
||||
ass = deepcopy(name_del)
|
||||
self.assertTrue(ass.lhs)
|
||||
self.assertTrue(ass.rhs)
|
||||
self.assertTrue(ass.entry)
|
||||
self.assertEqual(ass.pos, name_del.pos)
|
||||
self.assertFalse(ass.is_arg)
|
||||
self.assertTrue(ass.is_deletion)
|
||||
@@ -0,0 +1,129 @@
|
||||
# mode: run
|
||||
# tag: syntax
|
||||
|
||||
"""
|
||||
Uses TreeFragment to test invalid syntax.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from ...TestUtils import CythonTest
|
||||
from ..Errors import CompileError
|
||||
from .. import ExprNodes
|
||||
|
||||
# Copied from CPython's test_grammar.py
|
||||
VALID_UNDERSCORE_LITERALS = [
|
||||
'0_0_0',
|
||||
'4_2',
|
||||
'1_0000_0000',
|
||||
'0b1001_0100',
|
||||
'0xffff_ffff',
|
||||
'0o5_7_7',
|
||||
'1_00_00.5',
|
||||
'1_00_00.5j',
|
||||
'1_00_00.5e5',
|
||||
'1_00_00j',
|
||||
'1_00_00e5_1',
|
||||
'1e1_0',
|
||||
'.1_4',
|
||||
'.1_4e1',
|
||||
'.1_4j',
|
||||
]
|
||||
|
||||
# Copied from CPython's test_grammar.py
|
||||
INVALID_UNDERSCORE_LITERALS = [
|
||||
# Trailing underscores:
|
||||
'0_',
|
||||
'42_',
|
||||
'1.4j_',
|
||||
'0b1_',
|
||||
'0xf_',
|
||||
'0o5_',
|
||||
# Underscores in the base selector:
|
||||
'0_b0',
|
||||
'0_xf',
|
||||
'0_o5',
|
||||
# Underscore right after the base selector:
|
||||
'0b_0',
|
||||
'0x_f',
|
||||
'0o_5',
|
||||
# Old-style octal, still disallowed:
|
||||
#'0_7',
|
||||
#'09_99',
|
||||
# Special case with exponent:
|
||||
'0 if 1_Else 1',
|
||||
# Underscore right before a dot:
|
||||
'1_.4',
|
||||
'1_.4j',
|
||||
# Underscore right after a dot:
|
||||
'1._4',
|
||||
'1._4j',
|
||||
'._5',
|
||||
# Underscore right after a sign:
|
||||
'1.0e+_1',
|
||||
# Multiple consecutive underscores:
|
||||
'4_______2',
|
||||
'0.1__4',
|
||||
'0b1001__0100',
|
||||
'0xffff__ffff',
|
||||
'0o5__77',
|
||||
'1e1__0',
|
||||
# Underscore right before j:
|
||||
'1.4_j',
|
||||
'1.4e5_j',
|
||||
# Underscore right before e:
|
||||
'1_e1',
|
||||
'1.4_e1',
|
||||
# Underscore right after e:
|
||||
'1e_1',
|
||||
'1.4e_1',
|
||||
# Whitespace in literals
|
||||
'1_ 2',
|
||||
'1 _2',
|
||||
'1_2.2_ 1',
|
||||
'1_2.2 _1',
|
||||
'1_2e _1',
|
||||
'1_2e2 _1',
|
||||
'1_2e 2_1',
|
||||
]
|
||||
|
||||
|
||||
class TestGrammar(CythonTest):
|
||||
|
||||
def test_invalid_number_literals(self):
|
||||
for literal in INVALID_UNDERSCORE_LITERALS:
|
||||
for expression in ['%s', '1 + %s', '%s + 1', '2 * %s', '%s * 2']:
|
||||
code = 'x = ' + expression % literal
|
||||
try:
|
||||
self.fragment(u'''\
|
||||
# cython: language_level=3
|
||||
''' + code)
|
||||
except CompileError as exc:
|
||||
assert code in [s.strip() for s in str(exc).splitlines()], str(exc)
|
||||
else:
|
||||
assert False, "Invalid Cython code '%s' failed to raise an exception" % code
|
||||
|
||||
def test_valid_number_literals(self):
|
||||
for literal in VALID_UNDERSCORE_LITERALS:
|
||||
for i, expression in enumerate(['%s', '1 + %s', '%s + 1', '2 * %s', '%s * 2']):
|
||||
code = 'x = ' + expression % literal
|
||||
node = self.fragment(u'''\
|
||||
# cython: language_level=3
|
||||
''' + code).root
|
||||
assert node is not None
|
||||
|
||||
literal_node = node.stats[0].rhs # StatListNode([SingleAssignmentNode('x', expr)])
|
||||
if i > 0:
|
||||
# Add/MulNode() -> literal is first or second operand
|
||||
literal_node = literal_node.operand2 if i % 2 else literal_node.operand1
|
||||
if 'j' in literal or 'J' in literal:
|
||||
assert isinstance(literal_node, ExprNodes.ImagNode)
|
||||
elif '.' in literal or 'e' in literal or 'E' in literal and not ('0x' in literal or '0X' in literal):
|
||||
assert isinstance(literal_node, ExprNodes.FloatNode)
|
||||
else:
|
||||
assert isinstance(literal_node, ExprNodes.IntNode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main()
|
||||
@@ -0,0 +1,71 @@
|
||||
from Cython.TestUtils import CythonTest
|
||||
import Cython.Compiler.Errors as Errors
|
||||
from Cython.Compiler.Nodes import *
|
||||
from Cython.Compiler.ParseTreeTransforms import *
|
||||
from Cython.Compiler.Buffer import *
|
||||
|
||||
|
||||
class TestMemviewParsing(CythonTest):
|
||||
|
||||
def parse(self, s):
|
||||
return self.should_not_fail(lambda: self.fragment(s)).root
|
||||
|
||||
def not_parseable(self, expected_error, s):
|
||||
e = self.should_fail(lambda: self.fragment(s), Errors.CompileError)
|
||||
self.assertEqual(expected_error, e.message_only)
|
||||
|
||||
def test_default_1dim(self):
|
||||
self.parse(u"cdef int[:] x")
|
||||
self.parse(u"cdef short int[:] x")
|
||||
|
||||
def test_default_ndim(self):
|
||||
self.parse(u"cdef int[:,:,:,:,:] x")
|
||||
self.parse(u"cdef unsigned long int[:,:,:,:,:] x")
|
||||
self.parse(u"cdef unsigned int[:,:,:,:,:] x")
|
||||
|
||||
def test_zero_offset(self):
|
||||
self.parse(u"cdef long double[0:] x")
|
||||
self.parse(u"cdef int[0:] x")
|
||||
|
||||
def test_zero_offset_ndim(self):
|
||||
self.parse(u"cdef int[0:,0:,0:,0:] x")
|
||||
|
||||
def test_def_arg(self):
|
||||
self.parse(u"def foo(int[:,:] x): pass")
|
||||
|
||||
def test_cdef_arg(self):
|
||||
self.parse(u"cdef foo(int[:,:] x): pass")
|
||||
|
||||
def test_general_slice(self):
|
||||
self.parse(u'cdef float[::ptr, ::direct & contig, 0::full & strided] x')
|
||||
|
||||
def test_non_slice_memview(self):
|
||||
self.not_parseable(u"An axis specification in memoryview declaration does not have a ':'.",
|
||||
u"cdef double[:foo, bar] x")
|
||||
self.not_parseable(u"An axis specification in memoryview declaration does not have a ':'.",
|
||||
u"cdef double[0:foo, bar] x")
|
||||
|
||||
def test_basic(self):
|
||||
t = self.parse(u"cdef int[:] x")
|
||||
memv_node = t.stats[0].base_type
|
||||
self.assertTrue(isinstance(memv_node, MemoryViewSliceTypeNode))
|
||||
|
||||
# we also test other similar declarations (buffers, anonymous C arrays)
|
||||
# since the parsing has to distinguish between them.
|
||||
|
||||
def disable_test_no_buf_arg(self): # TODO
|
||||
self.not_parseable(u"Expected ']'",
|
||||
u"cdef extern foo(object[int, ndim=2])")
|
||||
|
||||
def disable_test_parse_sizeof(self): # TODO
|
||||
self.parse(u"sizeof(int[NN])")
|
||||
self.parse(u"sizeof(int[])")
|
||||
self.parse(u"sizeof(int[][NN])")
|
||||
self.not_parseable(u"Expected an identifier or literal",
|
||||
u"sizeof(int[:NN])")
|
||||
self.not_parseable(u"Expected ']'",
|
||||
u"sizeof(foo[dtype=bar]")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main()
|
||||
@@ -0,0 +1,289 @@
|
||||
import os.path
|
||||
import unittest
|
||||
|
||||
from Cython.TestUtils import TransformTest
|
||||
from Cython.Compiler.ParseTreeTransforms import *
|
||||
from Cython.Compiler.ParseTreeTransforms import _calculate_pickle_checksums
|
||||
from Cython.Compiler.Nodes import *
|
||||
from Cython.Compiler import Main, Symtab
|
||||
|
||||
|
||||
class TestNormalizeTree(TransformTest):
|
||||
def test_parserbehaviour_is_what_we_coded_for(self):
|
||||
t = self.fragment(u"if x: y").root
|
||||
self.assertLines(u"""
|
||||
(root): StatListNode
|
||||
stats[0]: IfStatNode
|
||||
if_clauses[0]: IfClauseNode
|
||||
condition: NameNode
|
||||
body: ExprStatNode
|
||||
expr: NameNode
|
||||
""", self.treetypes(t))
|
||||
|
||||
def test_wrap_singlestat(self):
|
||||
t = self.run_pipeline([NormalizeTree(None)], u"if x: y")
|
||||
self.assertLines(u"""
|
||||
(root): StatListNode
|
||||
stats[0]: IfStatNode
|
||||
if_clauses[0]: IfClauseNode
|
||||
condition: NameNode
|
||||
body: StatListNode
|
||||
stats[0]: ExprStatNode
|
||||
expr: NameNode
|
||||
""", self.treetypes(t))
|
||||
|
||||
def test_wrap_multistat(self):
|
||||
t = self.run_pipeline([NormalizeTree(None)], u"""
|
||||
if z:
|
||||
x
|
||||
y
|
||||
""")
|
||||
self.assertLines(u"""
|
||||
(root): StatListNode
|
||||
stats[0]: IfStatNode
|
||||
if_clauses[0]: IfClauseNode
|
||||
condition: NameNode
|
||||
body: StatListNode
|
||||
stats[0]: ExprStatNode
|
||||
expr: NameNode
|
||||
stats[1]: ExprStatNode
|
||||
expr: NameNode
|
||||
""", self.treetypes(t))
|
||||
|
||||
def test_statinexpr(self):
|
||||
t = self.run_pipeline([NormalizeTree(None)], u"""
|
||||
a, b = x, y
|
||||
""")
|
||||
self.assertLines(u"""
|
||||
(root): StatListNode
|
||||
stats[0]: SingleAssignmentNode
|
||||
lhs: TupleNode
|
||||
args[0]: NameNode
|
||||
args[1]: NameNode
|
||||
rhs: TupleNode
|
||||
args[0]: NameNode
|
||||
args[1]: NameNode
|
||||
""", self.treetypes(t))
|
||||
|
||||
def test_wrap_offagain(self):
|
||||
t = self.run_pipeline([NormalizeTree(None)], u"""
|
||||
x
|
||||
y
|
||||
if z:
|
||||
x
|
||||
""")
|
||||
self.assertLines(u"""
|
||||
(root): StatListNode
|
||||
stats[0]: ExprStatNode
|
||||
expr: NameNode
|
||||
stats[1]: ExprStatNode
|
||||
expr: NameNode
|
||||
stats[2]: IfStatNode
|
||||
if_clauses[0]: IfClauseNode
|
||||
condition: NameNode
|
||||
body: StatListNode
|
||||
stats[0]: ExprStatNode
|
||||
expr: NameNode
|
||||
""", self.treetypes(t))
|
||||
|
||||
|
||||
def test_pass_eliminated(self):
|
||||
t = self.run_pipeline([NormalizeTree(None)], u"pass")
|
||||
self.assertTrue(len(t.stats) == 0)
|
||||
|
||||
class TestWithTransform(object): # (TransformTest): # Disabled!
|
||||
|
||||
def test_simplified(self):
|
||||
t = self.run_pipeline([WithTransform(None)], u"""
|
||||
with x:
|
||||
y = z ** 3
|
||||
""")
|
||||
|
||||
self.assertCode(u"""
|
||||
|
||||
$0_0 = x
|
||||
$0_2 = $0_0.__exit__
|
||||
$0_0.__enter__()
|
||||
$0_1 = True
|
||||
try:
|
||||
try:
|
||||
$1_0 = None
|
||||
y = z ** 3
|
||||
except:
|
||||
$0_1 = False
|
||||
if (not $0_2($1_0)):
|
||||
raise
|
||||
finally:
|
||||
if $0_1:
|
||||
$0_2(None, None, None)
|
||||
|
||||
""", t)
|
||||
|
||||
def test_basic(self):
|
||||
t = self.run_pipeline([WithTransform(None)], u"""
|
||||
with x as y:
|
||||
y = z ** 3
|
||||
""")
|
||||
self.assertCode(u"""
|
||||
|
||||
$0_0 = x
|
||||
$0_2 = $0_0.__exit__
|
||||
$0_3 = $0_0.__enter__()
|
||||
$0_1 = True
|
||||
try:
|
||||
try:
|
||||
$1_0 = None
|
||||
y = $0_3
|
||||
y = z ** 3
|
||||
except:
|
||||
$0_1 = False
|
||||
if (not $0_2($1_0)):
|
||||
raise
|
||||
finally:
|
||||
if $0_1:
|
||||
$0_2(None, None, None)
|
||||
|
||||
""", t)
|
||||
|
||||
|
||||
class TestInterpretCompilerDirectives(TransformTest):
|
||||
"""
|
||||
This class tests the parallel directives AST-rewriting and importing.
|
||||
"""
|
||||
|
||||
# Test the parallel directives (c)importing
|
||||
|
||||
import_code = u"""
|
||||
cimport cython.parallel
|
||||
cimport cython.parallel as par
|
||||
from cython cimport parallel as par2
|
||||
from cython cimport parallel
|
||||
|
||||
from cython.parallel cimport threadid as tid
|
||||
from cython.parallel cimport threadavailable as tavail
|
||||
from cython.parallel cimport prange
|
||||
"""
|
||||
|
||||
expected_directives_dict = {
|
||||
u'cython.parallel': u'cython.parallel',
|
||||
u'par': u'cython.parallel',
|
||||
u'par2': u'cython.parallel',
|
||||
u'parallel': u'cython.parallel',
|
||||
|
||||
u"tid": u"cython.parallel.threadid",
|
||||
u"tavail": u"cython.parallel.threadavailable",
|
||||
u"prange": u"cython.parallel.prange",
|
||||
}
|
||||
|
||||
|
||||
def setUp(self):
|
||||
super(TestInterpretCompilerDirectives, self).setUp()
|
||||
|
||||
compilation_options = Main.CompilationOptions(Main.default_options)
|
||||
ctx = compilation_options.create_context()
|
||||
|
||||
transform = InterpretCompilerDirectives(ctx, ctx.compiler_directives)
|
||||
transform.module_scope = Symtab.ModuleScope('__main__', None, ctx)
|
||||
self.pipeline = [transform]
|
||||
|
||||
self.debug_exception_on_error = DebugFlags.debug_exception_on_error
|
||||
|
||||
def tearDown(self):
|
||||
DebugFlags.debug_exception_on_error = self.debug_exception_on_error
|
||||
|
||||
def test_parallel_directives_cimports(self):
|
||||
self.run_pipeline(self.pipeline, self.import_code)
|
||||
parallel_directives = self.pipeline[0].parallel_directives
|
||||
self.assertEqual(parallel_directives, self.expected_directives_dict)
|
||||
|
||||
def test_parallel_directives_imports(self):
|
||||
self.run_pipeline(self.pipeline,
|
||||
self.import_code.replace(u'cimport', u'import'))
|
||||
parallel_directives = self.pipeline[0].parallel_directives
|
||||
self.assertEqual(parallel_directives, self.expected_directives_dict)
|
||||
|
||||
|
||||
# TODO: Re-enable once they're more robust.
|
||||
if False:
|
||||
from Cython.Debugger import DebugWriter
|
||||
from Cython.Debugger.Tests.TestLibCython import DebuggerTestCase
|
||||
else:
|
||||
# skip test, don't let it inherit unittest.TestCase
|
||||
DebuggerTestCase = object
|
||||
|
||||
|
||||
class TestDebugTransform(DebuggerTestCase):
|
||||
|
||||
def elem_hasattrs(self, elem, attrs):
|
||||
return all(attr in elem.attrib for attr in attrs)
|
||||
|
||||
def test_debug_info(self):
|
||||
try:
|
||||
assert os.path.exists(self.debug_dest)
|
||||
|
||||
t = DebugWriter.etree.parse(self.debug_dest)
|
||||
# the xpath of the standard ElementTree is primitive, don't use
|
||||
# anything fancy
|
||||
L = list(t.find('/Module/Globals'))
|
||||
assert L
|
||||
xml_globals = dict((e.attrib['name'], e.attrib['type']) for e in L)
|
||||
self.assertEqual(len(L), len(xml_globals))
|
||||
|
||||
L = list(t.find('/Module/Functions'))
|
||||
assert L
|
||||
xml_funcs = dict((e.attrib['qualified_name'], e) for e in L)
|
||||
self.assertEqual(len(L), len(xml_funcs))
|
||||
|
||||
# test globals
|
||||
self.assertEqual('CObject', xml_globals.get('c_var'))
|
||||
self.assertEqual('PythonObject', xml_globals.get('python_var'))
|
||||
|
||||
# test functions
|
||||
funcnames = ('codefile.spam', 'codefile.ham', 'codefile.eggs',
|
||||
'codefile.closure', 'codefile.inner')
|
||||
required_xml_attrs = 'name', 'cname', 'qualified_name'
|
||||
assert all(f in xml_funcs for f in funcnames)
|
||||
spam, ham, eggs = [xml_funcs[funcname] for funcname in funcnames]
|
||||
|
||||
self.assertEqual(spam.attrib['name'], 'spam')
|
||||
self.assertNotEqual('spam', spam.attrib['cname'])
|
||||
assert self.elem_hasattrs(spam, required_xml_attrs)
|
||||
|
||||
# test locals of functions
|
||||
spam_locals = list(spam.find('Locals'))
|
||||
assert spam_locals
|
||||
spam_locals.sort(key=lambda e: e.attrib['name'])
|
||||
names = [e.attrib['name'] for e in spam_locals]
|
||||
self.assertEqual(list('abcd'), names)
|
||||
assert self.elem_hasattrs(spam_locals[0], required_xml_attrs)
|
||||
|
||||
# test arguments of functions
|
||||
spam_arguments = list(spam.find('Arguments'))
|
||||
assert spam_arguments
|
||||
self.assertEqual(1, len(list(spam_arguments)))
|
||||
|
||||
# test step-into functions
|
||||
step_into = spam.find('StepIntoFunctions')
|
||||
spam_stepinto = [x.attrib['name'] for x in step_into]
|
||||
assert spam_stepinto
|
||||
self.assertEqual(2, len(spam_stepinto))
|
||||
assert 'puts' in spam_stepinto
|
||||
assert 'some_c_function' in spam_stepinto
|
||||
except:
|
||||
f = open(self.debug_dest)
|
||||
try:
|
||||
print(f.read())
|
||||
finally:
|
||||
f.close()
|
||||
raise
|
||||
|
||||
|
||||
class TestAnalyseDeclarationsTransform(unittest.TestCase):
|
||||
def test_calculate_pickle_checksums(self):
|
||||
checksums = _calculate_pickle_checksums(['member1', 'member2', 'member3'])
|
||||
assert 2 <= len(checksums) <= 3, checksums # expecting ['0xc0af380' (MD5), '0x0c75bd4', '0xa7a7b94']
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main()
|
||||
@@ -0,0 +1,73 @@
|
||||
import unittest
|
||||
|
||||
from Cython.Compiler import PyrexTypes as pt
|
||||
from Cython.Compiler.ExprNodes import NameNode
|
||||
from Cython.Compiler.PyrexTypes import CFuncTypeArg
|
||||
|
||||
def cfunctype(*arg_types):
|
||||
return pt.CFuncType(pt.c_int_type,
|
||||
[ CFuncTypeArg("name", arg_type, None) for arg_type in arg_types ])
|
||||
|
||||
def cppclasstype(name, base_classes):
|
||||
return pt.CppClassType(name, None, 'CPP_'+name, base_classes)
|
||||
|
||||
class SignatureMatcherTest(unittest.TestCase):
|
||||
"""
|
||||
Test the signature matching algorithm for overloaded signatures.
|
||||
"""
|
||||
def assertMatches(self, expected_type, arg_types, functions):
|
||||
match = pt.best_match(arg_types, functions)
|
||||
if expected_type is not None:
|
||||
self.assertNotEqual(None, match)
|
||||
self.assertEqual(expected_type, match.type)
|
||||
|
||||
def test_cpp_reference_single_arg(self):
|
||||
function_types = [
|
||||
cfunctype(pt.CReferenceType(pt.c_int_type)),
|
||||
cfunctype(pt.CReferenceType(pt.c_long_type)),
|
||||
cfunctype(pt.CReferenceType(pt.c_double_type)),
|
||||
]
|
||||
|
||||
functions = [ NameNode(None, type=t) for t in function_types ]
|
||||
self.assertMatches(function_types[0], [pt.c_int_type], functions)
|
||||
self.assertMatches(function_types[1], [pt.c_long_type], functions)
|
||||
self.assertMatches(function_types[2], [pt.c_double_type], functions)
|
||||
|
||||
def test_cpp_reference_two_args(self):
|
||||
function_types = [
|
||||
cfunctype(
|
||||
pt.CReferenceType(pt.c_int_type), pt.CReferenceType(pt.c_long_type)),
|
||||
cfunctype(
|
||||
pt.CReferenceType(pt.c_long_type), pt.CReferenceType(pt.c_long_type)),
|
||||
]
|
||||
|
||||
functions = [ NameNode(None, type=t) for t in function_types ]
|
||||
self.assertMatches(function_types[0], [pt.c_int_type, pt.c_long_type], functions)
|
||||
self.assertMatches(function_types[1], [pt.c_long_type, pt.c_long_type], functions)
|
||||
self.assertMatches(function_types[1], [pt.c_long_type, pt.c_int_type], functions)
|
||||
|
||||
def test_cpp_reference_cpp_class(self):
|
||||
classes = [ cppclasstype("Test%d"%i, []) for i in range(2) ]
|
||||
function_types = [
|
||||
cfunctype(pt.CReferenceType(classes[0])),
|
||||
cfunctype(pt.CReferenceType(classes[1])),
|
||||
]
|
||||
|
||||
functions = [ NameNode(None, type=t) for t in function_types ]
|
||||
self.assertMatches(function_types[0], [classes[0]], functions)
|
||||
self.assertMatches(function_types[1], [classes[1]], functions)
|
||||
|
||||
def test_cpp_reference_cpp_class_and_int(self):
|
||||
classes = [ cppclasstype("Test%d"%i, []) for i in range(2) ]
|
||||
function_types = [
|
||||
cfunctype(pt.CReferenceType(classes[0]), pt.c_int_type),
|
||||
cfunctype(pt.CReferenceType(classes[0]), pt.c_long_type),
|
||||
cfunctype(pt.CReferenceType(classes[1]), pt.c_int_type),
|
||||
cfunctype(pt.CReferenceType(classes[1]), pt.c_long_type),
|
||||
]
|
||||
|
||||
functions = [ NameNode(None, type=t) for t in function_types ]
|
||||
self.assertMatches(function_types[0], [classes[0], pt.c_int_type], functions)
|
||||
self.assertMatches(function_types[1], [classes[0], pt.c_long_type], functions)
|
||||
self.assertMatches(function_types[2], [classes[1], pt.c_int_type], functions)
|
||||
self.assertMatches(function_types[3], [classes[1], pt.c_long_type], functions)
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import Cython.Compiler.StringEncoding as StringEncoding
|
||||
|
||||
|
||||
class StringEncodingTest(unittest.TestCase):
|
||||
"""
|
||||
Test the StringEncoding module.
|
||||
"""
|
||||
def test_string_contains_lone_surrogates(self):
|
||||
self.assertFalse(StringEncoding.string_contains_lone_surrogates(u"abc"))
|
||||
self.assertFalse(StringEncoding.string_contains_lone_surrogates(u"\uABCD"))
|
||||
self.assertFalse(StringEncoding.string_contains_lone_surrogates(u"\N{SNOWMAN}"))
|
||||
|
||||
# This behaves differently in Py2 when freshly parsed and read from a .pyc file,
|
||||
# but it seems to be a marshalling bug in Py2, which doesn't hurt us in Cython.
|
||||
if sys.version_info[0] != 2:
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(u"\uD800\uDFFF"))
|
||||
|
||||
# In Py2 with 16bit Unicode, the following is indistinguishable from the 32bit character.
|
||||
obfuscated_surrogate_pair = (u"\uDFFF" + "\uD800")[::-1]
|
||||
if sys.version_info[0] == 2 and sys.maxunicode == 65565:
|
||||
self.assertFalse(StringEncoding.string_contains_lone_surrogates(obfuscated_surrogate_pair))
|
||||
else:
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(obfuscated_surrogate_pair))
|
||||
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(u"\uD800"))
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(u"\uDFFF"))
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(u"\uDFFF\uD800"))
|
||||
self.assertTrue(StringEncoding.string_contains_lone_surrogates(u"\uD800x\uDFFF"))
|
||||
|
||||
def test_string_contains_surrogates(self):
|
||||
self.assertFalse(StringEncoding.string_contains_surrogates(u"abc"))
|
||||
self.assertFalse(StringEncoding.string_contains_surrogates(u"\uABCD"))
|
||||
self.assertFalse(StringEncoding.string_contains_surrogates(u"\N{SNOWMAN}"))
|
||||
|
||||
self.assertTrue(StringEncoding.string_contains_surrogates(u"\uD800"))
|
||||
self.assertTrue(StringEncoding.string_contains_surrogates(u"\uDFFF"))
|
||||
self.assertTrue(StringEncoding.string_contains_surrogates(u"\uD800\uDFFF"))
|
||||
self.assertTrue(StringEncoding.string_contains_surrogates(u"\uDFFF\uD800"))
|
||||
self.assertTrue(StringEncoding.string_contains_surrogates(u"\uD800x\uDFFF"))
|
||||
@@ -0,0 +1,64 @@
|
||||
from Cython.TestUtils import CythonTest
|
||||
from Cython.Compiler.TreeFragment import *
|
||||
from Cython.Compiler.Nodes import *
|
||||
from Cython.Compiler.UtilNodes import *
|
||||
import Cython.Compiler.Naming as Naming
|
||||
|
||||
class TestTreeFragments(CythonTest):
|
||||
|
||||
def test_basic(self):
|
||||
F = self.fragment(u"x = 4")
|
||||
T = F.copy()
|
||||
self.assertCode(u"x = 4", T)
|
||||
|
||||
def test_copy_is_taken(self):
|
||||
F = self.fragment(u"if True: x = 4")
|
||||
T1 = F.root
|
||||
T2 = F.copy()
|
||||
self.assertEqual("x", T2.stats[0].if_clauses[0].body.lhs.name)
|
||||
T2.stats[0].if_clauses[0].body.lhs.name = "other"
|
||||
self.assertEqual("x", T1.stats[0].if_clauses[0].body.lhs.name)
|
||||
|
||||
def test_substitutions_are_copied(self):
|
||||
T = self.fragment(u"y + y").substitute({"y": NameNode(pos=None, name="x")})
|
||||
self.assertEqual("x", T.stats[0].expr.operand1.name)
|
||||
self.assertEqual("x", T.stats[0].expr.operand2.name)
|
||||
self.assertTrue(T.stats[0].expr.operand1 is not T.stats[0].expr.operand2)
|
||||
|
||||
def test_substitution(self):
|
||||
F = self.fragment(u"x = 4")
|
||||
y = NameNode(pos=None, name=u"y")
|
||||
T = F.substitute({"x" : y})
|
||||
self.assertCode(u"y = 4", T)
|
||||
|
||||
def test_exprstat(self):
|
||||
F = self.fragment(u"PASS")
|
||||
pass_stat = PassStatNode(pos=None)
|
||||
T = F.substitute({"PASS" : pass_stat})
|
||||
self.assertTrue(isinstance(T.stats[0], PassStatNode), T)
|
||||
|
||||
def test_pos_is_transferred(self):
|
||||
F = self.fragment(u"""
|
||||
x = y
|
||||
x = u * v ** w
|
||||
""")
|
||||
T = F.substitute({"v" : NameNode(pos=None, name="a")})
|
||||
v = F.root.stats[1].rhs.operand2.operand1
|
||||
a = T.stats[1].rhs.operand2.operand1
|
||||
self.assertEqual(v.pos, a.pos)
|
||||
|
||||
def test_temps(self):
|
||||
TemplateTransform.temp_name_counter = 0
|
||||
F = self.fragment(u"""
|
||||
TMP
|
||||
x = TMP
|
||||
""")
|
||||
T = F.substitute(temps=[u"TMP"])
|
||||
s = T.body.stats
|
||||
self.assertTrue(isinstance(s[0].expr, TempRefNode))
|
||||
self.assertTrue(isinstance(s[1].rhs, TempRefNode))
|
||||
self.assertTrue(s[0].expr.handle is s[1].rhs.handle)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main()
|
||||
@@ -0,0 +1,94 @@
|
||||
import unittest
|
||||
from Cython.Compiler.Visitor import PrintTree
|
||||
from Cython.TestUtils import TransformTest
|
||||
from Cython.Compiler.TreePath import find_first, find_all
|
||||
from Cython.Compiler import Nodes, ExprNodes
|
||||
|
||||
class TestTreePath(TransformTest):
|
||||
_tree = None
|
||||
|
||||
def _build_tree(self):
|
||||
if self._tree is None:
|
||||
self._tree = self.run_pipeline([], u"""
|
||||
def decorator(fun): # DefNode
|
||||
return fun # ReturnStatNode, NameNode
|
||||
@decorator # NameNode
|
||||
def decorated(): # DefNode
|
||||
pass
|
||||
""")
|
||||
return self._tree
|
||||
|
||||
def test_node_path(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(2, len(find_all(t, "//DefNode")))
|
||||
self.assertEqual(2, len(find_all(t, "//NameNode")))
|
||||
self.assertEqual(1, len(find_all(t, "//ReturnStatNode")))
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode//ReturnStatNode")))
|
||||
|
||||
def test_node_path_star(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(10, len(find_all(t, "//*")))
|
||||
self.assertEqual(8, len(find_all(t, "//DefNode//*")))
|
||||
self.assertEqual(0, len(find_all(t, "//NameNode//*")))
|
||||
|
||||
def test_node_path_attribute(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(2, len(find_all(t, "//NameNode/@name")))
|
||||
self.assertEqual(['fun', 'decorator'], find_all(t, "//NameNode/@name"))
|
||||
|
||||
def test_node_path_attribute_dotted(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(1, len(find_all(t, "//ReturnStatNode/@value.name")))
|
||||
self.assertEqual(['fun'], find_all(t, "//ReturnStatNode/@value.name"))
|
||||
|
||||
def test_node_path_child(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode/ReturnStatNode/NameNode")))
|
||||
self.assertEqual(1, len(find_all(t, "//ReturnStatNode/NameNode")))
|
||||
|
||||
def test_node_path_node_predicate(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(0, len(find_all(t, "//DefNode[.//ForInStatNode]")))
|
||||
self.assertEqual(2, len(find_all(t, "//DefNode[.//NameNode]")))
|
||||
self.assertEqual(1, len(find_all(t, "//ReturnStatNode[./NameNode]")))
|
||||
self.assertEqual(Nodes.ReturnStatNode,
|
||||
type(find_first(t, "//ReturnStatNode[./NameNode]")))
|
||||
|
||||
def test_node_path_node_predicate_step(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(2, len(find_all(t, "//DefNode[.//NameNode]")))
|
||||
self.assertEqual(8, len(find_all(t, "//DefNode[.//NameNode]//*")))
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode[.//NameNode]//ReturnStatNode")))
|
||||
self.assertEqual(Nodes.ReturnStatNode,
|
||||
type(find_first(t, "//DefNode[.//NameNode]//ReturnStatNode")))
|
||||
|
||||
def test_node_path_attribute_exists(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(2, len(find_all(t, "//NameNode[@name]")))
|
||||
self.assertEqual(ExprNodes.NameNode,
|
||||
type(find_first(t, "//NameNode[@name]")))
|
||||
|
||||
def test_node_path_attribute_exists_not(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(0, len(find_all(t, "//NameNode[not(@name)]")))
|
||||
self.assertEqual(2, len(find_all(t, "//NameNode[not(@honking)]")))
|
||||
|
||||
def test_node_path_and(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode[.//ReturnStatNode and .//NameNode]")))
|
||||
self.assertEqual(0, len(find_all(t, "//NameNode[@honking and @name]")))
|
||||
self.assertEqual(0, len(find_all(t, "//NameNode[@name and @honking]")))
|
||||
self.assertEqual(2, len(find_all(t, "//DefNode[.//NameNode[@name] and @name]")))
|
||||
|
||||
def test_node_path_attribute_string_predicate(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(1, len(find_all(t, "//NameNode[@name = 'decorator']")))
|
||||
|
||||
def test_node_path_recursive_predicate(self):
|
||||
t = self._build_tree()
|
||||
self.assertEqual(2, len(find_all(t, "//DefNode[.//NameNode[@name]]")))
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode[.//NameNode[@name = 'decorator']]")))
|
||||
self.assertEqual(1, len(find_all(t, "//DefNode[.//ReturnStatNode[./NameNode[@name = 'fun']]/NameNode]")))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,19 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
import Cython.Compiler.PyrexTypes as PT
|
||||
|
||||
|
||||
class TestMethodDispatcherTransform(unittest.TestCase):
|
||||
|
||||
def test_widest_numeric_type(self):
|
||||
def assert_widest(type1, type2, widest):
|
||||
self.assertEqual(widest, PT.widest_numeric_type(type1, type2))
|
||||
|
||||
assert_widest(PT.c_int_type, PT.c_long_type, PT.c_long_type)
|
||||
assert_widest(PT.c_double_type, PT.c_long_type, PT.c_double_type)
|
||||
assert_widest(PT.c_longdouble_type, PT.c_long_type, PT.c_longdouble_type)
|
||||
|
||||
cenum = PT.CEnumType("E", "cenum", typedef_flag=False)
|
||||
assert_widest(PT.c_int_type, cenum, PT.c_int_type)
|
||||
@@ -0,0 +1,101 @@
|
||||
import unittest
|
||||
|
||||
from Cython.Compiler import Code, UtilityCode
|
||||
|
||||
|
||||
def strip_2tup(tup):
|
||||
return tup[0] and tup[0].strip(), tup[1] and tup[1].strip()
|
||||
|
||||
class TestUtilityLoader(unittest.TestCase):
|
||||
"""
|
||||
Test loading UtilityCodes
|
||||
"""
|
||||
|
||||
expected = "test {{loader}} prototype", "test {{loader}} impl"
|
||||
|
||||
required = "req {{loader}} proto", "req {{loader}} impl"
|
||||
|
||||
context = dict(loader='Loader')
|
||||
|
||||
name = "TestUtilityLoader"
|
||||
filename = "TestUtilityLoader.c"
|
||||
cls = Code.UtilityCode
|
||||
|
||||
def test_load_as_string(self):
|
||||
got = strip_2tup(self.cls.load_as_string(self.name))
|
||||
self.assertEqual(got, self.expected)
|
||||
|
||||
got = strip_2tup(self.cls.load_as_string(self.name, self.filename))
|
||||
self.assertEqual(got, self.expected)
|
||||
|
||||
def test_load(self):
|
||||
utility = self.cls.load(self.name)
|
||||
got = strip_2tup((utility.proto, utility.impl))
|
||||
self.assertEqual(got, self.expected)
|
||||
|
||||
required, = utility.requires
|
||||
got = strip_2tup((required.proto, required.impl))
|
||||
self.assertEqual(got, self.required)
|
||||
|
||||
utility = self.cls.load(self.name, from_file=self.filename)
|
||||
got = strip_2tup((utility.proto, utility.impl))
|
||||
self.assertEqual(got, self.expected)
|
||||
|
||||
utility = self.cls.load_cached(self.name, from_file=self.filename)
|
||||
got = strip_2tup((utility.proto, utility.impl))
|
||||
self.assertEqual(got, self.expected)
|
||||
|
||||
|
||||
class TestTempitaUtilityLoader(TestUtilityLoader):
|
||||
"""
|
||||
Test loading UtilityCodes with Tempita substitution
|
||||
"""
|
||||
expected_tempita = (TestUtilityLoader.expected[0].replace('{{loader}}', 'Loader'),
|
||||
TestUtilityLoader.expected[1].replace('{{loader}}', 'Loader'))
|
||||
|
||||
required_tempita = (TestUtilityLoader.required[0].replace('{{loader}}', 'Loader'),
|
||||
TestUtilityLoader.required[1].replace('{{loader}}', 'Loader'))
|
||||
|
||||
cls = Code.TempitaUtilityCode
|
||||
|
||||
def test_load_as_string(self):
|
||||
got = strip_2tup(self.cls.load_as_string(self.name, context=self.context))
|
||||
self.assertEqual(got, self.expected_tempita)
|
||||
|
||||
def test_load(self):
|
||||
utility = self.cls.load(self.name, context=self.context)
|
||||
got = strip_2tup((utility.proto, utility.impl))
|
||||
self.assertEqual(got, self.expected_tempita)
|
||||
|
||||
required, = utility.requires
|
||||
got = strip_2tup((required.proto, required.impl))
|
||||
self.assertEqual(got, self.required_tempita)
|
||||
|
||||
utility = self.cls.load(self.name, from_file=self.filename, context=self.context)
|
||||
got = strip_2tup((utility.proto, utility.impl))
|
||||
self.assertEqual(got, self.expected_tempita)
|
||||
|
||||
|
||||
class TestCythonUtilityLoader(TestTempitaUtilityLoader):
|
||||
"""
|
||||
Test loading CythonUtilityCodes
|
||||
"""
|
||||
|
||||
# Just change the attributes and run the same tests
|
||||
expected = None, "test {{cy_loader}} impl"
|
||||
expected_tempita = None, "test CyLoader impl"
|
||||
|
||||
required = None, "req {{cy_loader}} impl"
|
||||
required_tempita = None, "req CyLoader impl"
|
||||
|
||||
context = dict(cy_loader='CyLoader')
|
||||
|
||||
name = "TestCyUtilityLoader"
|
||||
filename = "TestCyUtilityLoader.pyx"
|
||||
cls = UtilityCode.CythonUtilityCode
|
||||
|
||||
# Small hack to pass our tests above
|
||||
cls.proto = None
|
||||
|
||||
test_load = TestUtilityLoader.test_load
|
||||
test_load_tempita = TestTempitaUtilityLoader.test_load
|
||||
@@ -0,0 +1,61 @@
|
||||
from Cython.Compiler.ModuleNode import ModuleNode
|
||||
from Cython.Compiler.Symtab import ModuleScope
|
||||
from Cython.TestUtils import TransformTest
|
||||
from Cython.Compiler.Visitor import MethodDispatcherTransform
|
||||
from Cython.Compiler.ParseTreeTransforms import (
|
||||
NormalizeTree, AnalyseDeclarationsTransform,
|
||||
AnalyseExpressionsTransform, InterpretCompilerDirectives)
|
||||
|
||||
|
||||
class TestMethodDispatcherTransform(TransformTest):
|
||||
_tree = None
|
||||
|
||||
def _build_tree(self):
|
||||
if self._tree is None:
|
||||
context = None
|
||||
|
||||
def fake_module(node):
|
||||
scope = ModuleScope('test', None, None)
|
||||
return ModuleNode(node.pos, doc=None, body=node,
|
||||
scope=scope, full_module_name='test',
|
||||
directive_comments={})
|
||||
pipeline = [
|
||||
fake_module,
|
||||
NormalizeTree(context),
|
||||
InterpretCompilerDirectives(context, {}),
|
||||
AnalyseDeclarationsTransform(context),
|
||||
AnalyseExpressionsTransform(context),
|
||||
]
|
||||
self._tree = self.run_pipeline(pipeline, u"""
|
||||
cdef bytes s = b'asdfg'
|
||||
cdef dict d = {1:2}
|
||||
x = s * 3
|
||||
d.get('test')
|
||||
""")
|
||||
return self._tree
|
||||
|
||||
def test_builtin_method(self):
|
||||
calls = [0]
|
||||
class Test(MethodDispatcherTransform):
|
||||
def _handle_simple_method_dict_get(self, node, func, args, unbound):
|
||||
calls[0] += 1
|
||||
return node
|
||||
|
||||
tree = self._build_tree()
|
||||
Test(None)(tree)
|
||||
self.assertEqual(1, calls[0])
|
||||
|
||||
def test_binop_method(self):
|
||||
calls = {'bytes': 0, 'object': 0}
|
||||
class Test(MethodDispatcherTransform):
|
||||
def _handle_simple_method_bytes___mul__(self, node, func, args, unbound):
|
||||
calls['bytes'] += 1
|
||||
return node
|
||||
def _handle_simple_method_object___mul__(self, node, func, args, unbound):
|
||||
calls['object'] += 1
|
||||
return node
|
||||
|
||||
tree = self._build_tree()
|
||||
Test(None)(tree)
|
||||
self.assertEqual(1, calls['bytes'])
|
||||
self.assertEqual(0, calls['object'])
|
||||
@@ -0,0 +1 @@
|
||||
# empty file
|
||||
@@ -0,0 +1,275 @@
|
||||
#
|
||||
# TreeFragments - parsing of strings to trees
|
||||
#
|
||||
|
||||
"""
|
||||
Support for parsing strings into code trees.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
from io import StringIO
|
||||
|
||||
from .Scanning import PyrexScanner, StringSourceDescriptor
|
||||
from .Symtab import ModuleScope
|
||||
from . import PyrexTypes
|
||||
from .Visitor import VisitorTransform
|
||||
from .Nodes import Node, StatListNode
|
||||
from .ExprNodes import NameNode
|
||||
from .StringEncoding import _unicode
|
||||
from . import Parsing
|
||||
from . import Main
|
||||
from . import UtilNodes
|
||||
|
||||
|
||||
class StringParseContext(Main.Context):
|
||||
def __init__(self, name, include_directories=None, compiler_directives=None, cpp=False):
|
||||
if include_directories is None:
|
||||
include_directories = []
|
||||
if compiler_directives is None:
|
||||
compiler_directives = {}
|
||||
# TODO: see if "language_level=3" also works for our internal code here.
|
||||
Main.Context.__init__(self, include_directories, compiler_directives, cpp=cpp, language_level=2)
|
||||
self.module_name = name
|
||||
|
||||
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, absolute_fallback=True):
|
||||
if module_name not in (self.module_name, 'cython'):
|
||||
raise AssertionError("Not yet supporting any cimports/includes from string code snippets")
|
||||
return ModuleScope(module_name, parent_module=None, context=self)
|
||||
|
||||
|
||||
def parse_from_strings(name, code, pxds=None, level=None, initial_pos=None,
|
||||
context=None, allow_struct_enum_decorator=False):
|
||||
"""
|
||||
Utility method to parse a (unicode) string of code. This is mostly
|
||||
used for internal Cython compiler purposes (creating code snippets
|
||||
that transforms should emit, as well as unit testing).
|
||||
|
||||
code - a unicode string containing Cython (module-level) code
|
||||
name - a descriptive name for the code source (to use in error messages etc.)
|
||||
|
||||
RETURNS
|
||||
|
||||
The tree, i.e. a ModuleNode. The ModuleNode's scope attribute is
|
||||
set to the scope used when parsing.
|
||||
"""
|
||||
if context is None:
|
||||
context = StringParseContext(name)
|
||||
# Since source files carry an encoding, it makes sense in this context
|
||||
# to use a unicode string so that code fragments don't have to bother
|
||||
# with encoding. This means that test code passed in should not have an
|
||||
# encoding header.
|
||||
assert isinstance(code, _unicode), "unicode code snippets only please"
|
||||
encoding = "UTF-8"
|
||||
|
||||
module_name = name
|
||||
if initial_pos is None:
|
||||
initial_pos = (name, 1, 0)
|
||||
code_source = StringSourceDescriptor(name, code)
|
||||
|
||||
scope = context.find_module(module_name, pos=initial_pos, need_pxd=False)
|
||||
|
||||
buf = StringIO(code)
|
||||
|
||||
scanner = PyrexScanner(buf, code_source, source_encoding = encoding,
|
||||
scope = scope, context = context, initial_pos = initial_pos)
|
||||
ctx = Parsing.Ctx(allow_struct_enum_decorator=allow_struct_enum_decorator)
|
||||
|
||||
if level is None:
|
||||
tree = Parsing.p_module(scanner, 0, module_name, ctx=ctx)
|
||||
tree.scope = scope
|
||||
tree.is_pxd = False
|
||||
else:
|
||||
tree = Parsing.p_code(scanner, level=level, ctx=ctx)
|
||||
|
||||
tree.scope = scope
|
||||
return tree
|
||||
|
||||
|
||||
class TreeCopier(VisitorTransform):
|
||||
def visit_Node(self, node):
|
||||
if node is None:
|
||||
return node
|
||||
else:
|
||||
c = node.clone_node()
|
||||
self.visitchildren(c)
|
||||
return c
|
||||
|
||||
|
||||
class ApplyPositionAndCopy(TreeCopier):
|
||||
def __init__(self, pos):
|
||||
super(ApplyPositionAndCopy, self).__init__()
|
||||
self.pos = pos
|
||||
|
||||
def visit_Node(self, node):
|
||||
copy = super(ApplyPositionAndCopy, self).visit_Node(node)
|
||||
copy.pos = self.pos
|
||||
return copy
|
||||
|
||||
|
||||
class TemplateTransform(VisitorTransform):
|
||||
"""
|
||||
Makes a copy of a template tree while doing substitutions.
|
||||
|
||||
A dictionary "substitutions" should be passed in when calling
|
||||
the transform; mapping names to replacement nodes. Then replacement
|
||||
happens like this:
|
||||
- If an ExprStatNode contains a single NameNode, whose name is
|
||||
a key in the substitutions dictionary, the ExprStatNode is
|
||||
replaced with a copy of the tree given in the dictionary.
|
||||
It is the responsibility of the caller that the replacement
|
||||
node is a valid statement.
|
||||
- If a single NameNode is otherwise encountered, it is replaced
|
||||
if its name is listed in the substitutions dictionary in the
|
||||
same way. It is the responsibility of the caller to make sure
|
||||
that the replacement nodes is a valid expression.
|
||||
|
||||
Also a list "temps" should be passed. Any names listed will
|
||||
be transformed into anonymous, temporary names.
|
||||
|
||||
Currently supported for tempnames is:
|
||||
NameNode
|
||||
(various function and class definition nodes etc. should be added to this)
|
||||
|
||||
Each replacement node gets the position of the substituted node
|
||||
recursively applied to every member node.
|
||||
"""
|
||||
|
||||
temp_name_counter = 0
|
||||
|
||||
def __call__(self, node, substitutions, temps, pos):
|
||||
self.substitutions = substitutions
|
||||
self.pos = pos
|
||||
tempmap = {}
|
||||
temphandles = []
|
||||
for temp in temps:
|
||||
TemplateTransform.temp_name_counter += 1
|
||||
handle = UtilNodes.TempHandle(PyrexTypes.py_object_type)
|
||||
tempmap[temp] = handle
|
||||
temphandles.append(handle)
|
||||
self.tempmap = tempmap
|
||||
result = super(TemplateTransform, self).__call__(node)
|
||||
if temps:
|
||||
result = UtilNodes.TempsBlockNode(self.get_pos(node),
|
||||
temps=temphandles,
|
||||
body=result)
|
||||
return result
|
||||
|
||||
def get_pos(self, node):
|
||||
if self.pos:
|
||||
return self.pos
|
||||
else:
|
||||
return node.pos
|
||||
|
||||
def visit_Node(self, node):
|
||||
if node is None:
|
||||
return None
|
||||
else:
|
||||
c = node.clone_node()
|
||||
if self.pos is not None:
|
||||
c.pos = self.pos
|
||||
self.visitchildren(c)
|
||||
return c
|
||||
|
||||
def try_substitution(self, node, key):
|
||||
sub = self.substitutions.get(key)
|
||||
if sub is not None:
|
||||
pos = self.pos
|
||||
if pos is None: pos = node.pos
|
||||
return ApplyPositionAndCopy(pos)(sub)
|
||||
else:
|
||||
return self.visit_Node(node) # make copy as usual
|
||||
|
||||
def visit_NameNode(self, node):
|
||||
temphandle = self.tempmap.get(node.name)
|
||||
if temphandle:
|
||||
# Replace name with temporary
|
||||
return temphandle.ref(self.get_pos(node))
|
||||
else:
|
||||
return self.try_substitution(node, node.name)
|
||||
|
||||
def visit_ExprStatNode(self, node):
|
||||
# If an expression-as-statement consists of only a replaceable
|
||||
# NameNode, we replace the entire statement, not only the NameNode
|
||||
if isinstance(node.expr, NameNode):
|
||||
return self.try_substitution(node, node.expr.name)
|
||||
else:
|
||||
return self.visit_Node(node)
|
||||
|
||||
|
||||
def copy_code_tree(node):
|
||||
return TreeCopier()(node)
|
||||
|
||||
|
||||
_match_indent = re.compile(u"^ *").match
|
||||
|
||||
|
||||
def strip_common_indent(lines):
|
||||
"""Strips empty lines and common indentation from the list of strings given in lines"""
|
||||
# TODO: Facilitate textwrap.indent instead
|
||||
lines = [x for x in lines if x.strip() != u""]
|
||||
if lines:
|
||||
minindent = min([len(_match_indent(x).group(0)) for x in lines])
|
||||
lines = [x[minindent:] for x in lines]
|
||||
return lines
|
||||
|
||||
|
||||
class TreeFragment(object):
|
||||
def __init__(self, code, name=None, pxds=None, temps=None, pipeline=None, level=None, initial_pos=None):
|
||||
if pxds is None:
|
||||
pxds = {}
|
||||
if temps is None:
|
||||
temps = []
|
||||
if pipeline is None:
|
||||
pipeline = []
|
||||
if not name:
|
||||
name = "(tree fragment)"
|
||||
|
||||
if isinstance(code, _unicode):
|
||||
def fmt(x): return u"\n".join(strip_common_indent(x.split(u"\n")))
|
||||
|
||||
fmt_code = fmt(code)
|
||||
fmt_pxds = {}
|
||||
for key, value in pxds.items():
|
||||
fmt_pxds[key] = fmt(value)
|
||||
mod = t = parse_from_strings(name, fmt_code, fmt_pxds, level=level, initial_pos=initial_pos)
|
||||
if level is None:
|
||||
t = t.body # Make sure a StatListNode is at the top
|
||||
if not isinstance(t, StatListNode):
|
||||
t = StatListNode(pos=mod.pos, stats=[t])
|
||||
for transform in pipeline:
|
||||
if transform is None:
|
||||
continue
|
||||
t = transform(t)
|
||||
self.root = t
|
||||
elif isinstance(code, Node):
|
||||
if pxds:
|
||||
raise NotImplementedError()
|
||||
self.root = code
|
||||
else:
|
||||
raise ValueError("Unrecognized code format (accepts unicode and Node)")
|
||||
self.temps = temps
|
||||
|
||||
def copy(self):
|
||||
return copy_code_tree(self.root)
|
||||
|
||||
def substitute(self, nodes=None, temps=None, pos = None):
|
||||
if nodes is None:
|
||||
nodes = {}
|
||||
if temps is None:
|
||||
temps = []
|
||||
return TemplateTransform()(self.root,
|
||||
substitutions = nodes,
|
||||
temps = self.temps + temps, pos = pos)
|
||||
|
||||
|
||||
class SetPosTransform(VisitorTransform):
|
||||
def __init__(self, pos):
|
||||
super(SetPosTransform, self).__init__()
|
||||
self.pos = pos
|
||||
|
||||
def visit_Node(self, node):
|
||||
node.pos = self.pos
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
@@ -0,0 +1,296 @@
|
||||
"""
|
||||
A simple XPath-like language for tree traversal.
|
||||
|
||||
This works by creating a filter chain of generator functions. Each
|
||||
function selects a part of the expression, e.g. a child node, a
|
||||
specific descendant or a node that holds an attribute.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import operator
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
_unicode = str
|
||||
else:
|
||||
_unicode = unicode
|
||||
|
||||
path_tokenizer = re.compile(
|
||||
r"("
|
||||
r"'[^']*'|\"[^\"]*\"|"
|
||||
r"//?|"
|
||||
r"\(\)|"
|
||||
r"==?|"
|
||||
r"[/.*\[\]()@])|"
|
||||
r"([^/\[\]()@=\s]+)|"
|
||||
r"\s+"
|
||||
).findall
|
||||
|
||||
def iterchildren(node, attr_name):
|
||||
# returns an iterable of all child nodes of that name
|
||||
child = getattr(node, attr_name)
|
||||
if child is not None:
|
||||
if type(child) is list:
|
||||
return child
|
||||
else:
|
||||
return [child]
|
||||
else:
|
||||
return ()
|
||||
|
||||
def _get_first_or_none(it):
|
||||
try:
|
||||
try:
|
||||
_next = it.next
|
||||
except AttributeError:
|
||||
return next(it)
|
||||
else:
|
||||
return _next()
|
||||
except StopIteration:
|
||||
return None
|
||||
|
||||
def type_name(node):
|
||||
return node.__class__.__name__.split('.')[-1]
|
||||
|
||||
def parse_func(next, token):
|
||||
name = token[1]
|
||||
token = next()
|
||||
if token[0] != '(':
|
||||
raise ValueError("Expected '(' after function name '%s'" % name)
|
||||
predicate = handle_predicate(next, token)
|
||||
return name, predicate
|
||||
|
||||
def handle_func_not(next, token):
|
||||
"""
|
||||
not(...)
|
||||
"""
|
||||
name, predicate = parse_func(next, token)
|
||||
|
||||
def select(result):
|
||||
for node in result:
|
||||
if _get_first_or_none(predicate([node])) is None:
|
||||
yield node
|
||||
return select
|
||||
|
||||
def handle_name(next, token):
|
||||
"""
|
||||
/NodeName/
|
||||
or
|
||||
func(...)
|
||||
"""
|
||||
name = token[1]
|
||||
if name in functions:
|
||||
return functions[name](next, token)
|
||||
def select(result):
|
||||
for node in result:
|
||||
for attr_name in node.child_attrs:
|
||||
for child in iterchildren(node, attr_name):
|
||||
if type_name(child) == name:
|
||||
yield child
|
||||
return select
|
||||
|
||||
def handle_star(next, token):
|
||||
"""
|
||||
/*/
|
||||
"""
|
||||
def select(result):
|
||||
for node in result:
|
||||
for name in node.child_attrs:
|
||||
for child in iterchildren(node, name):
|
||||
yield child
|
||||
return select
|
||||
|
||||
def handle_dot(next, token):
|
||||
"""
|
||||
/./
|
||||
"""
|
||||
def select(result):
|
||||
return result
|
||||
return select
|
||||
|
||||
def handle_descendants(next, token):
|
||||
"""
|
||||
//...
|
||||
"""
|
||||
token = next()
|
||||
if token[0] == "*":
|
||||
def iter_recursive(node):
|
||||
for name in node.child_attrs:
|
||||
for child in iterchildren(node, name):
|
||||
yield child
|
||||
for c in iter_recursive(child):
|
||||
yield c
|
||||
elif not token[0]:
|
||||
node_name = token[1]
|
||||
def iter_recursive(node):
|
||||
for name in node.child_attrs:
|
||||
for child in iterchildren(node, name):
|
||||
if type_name(child) == node_name:
|
||||
yield child
|
||||
for c in iter_recursive(child):
|
||||
yield c
|
||||
else:
|
||||
raise ValueError("Expected node name after '//'")
|
||||
|
||||
def select(result):
|
||||
for node in result:
|
||||
for child in iter_recursive(node):
|
||||
yield child
|
||||
|
||||
return select
|
||||
|
||||
|
||||
def handle_attribute(next, token):
|
||||
token = next()
|
||||
if token[0]:
|
||||
raise ValueError("Expected attribute name")
|
||||
name = token[1]
|
||||
value = None
|
||||
try:
|
||||
token = next()
|
||||
except StopIteration:
|
||||
pass
|
||||
else:
|
||||
if token[0] == '=':
|
||||
value = parse_path_value(next)
|
||||
readattr = operator.attrgetter(name)
|
||||
if value is None:
|
||||
def select(result):
|
||||
for node in result:
|
||||
try:
|
||||
attr_value = readattr(node)
|
||||
except AttributeError:
|
||||
continue
|
||||
if attr_value is not None:
|
||||
yield attr_value
|
||||
else:
|
||||
def select(result):
|
||||
for node in result:
|
||||
try:
|
||||
attr_value = readattr(node)
|
||||
except AttributeError:
|
||||
continue
|
||||
if attr_value == value:
|
||||
yield attr_value
|
||||
elif (isinstance(attr_value, bytes) and isinstance(value, _unicode) and
|
||||
attr_value == value.encode()):
|
||||
# allow a bytes-to-string comparison too
|
||||
yield attr_value
|
||||
|
||||
return select
|
||||
|
||||
|
||||
def parse_path_value(next):
|
||||
token = next()
|
||||
value = token[0]
|
||||
if value:
|
||||
if value[:1] == "'" or value[:1] == '"':
|
||||
return value[1:-1]
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
elif token[1].isdigit():
|
||||
return int(token[1])
|
||||
else:
|
||||
name = token[1].lower()
|
||||
if name == 'true':
|
||||
return True
|
||||
elif name == 'false':
|
||||
return False
|
||||
raise ValueError("Invalid attribute predicate: '%s'" % value)
|
||||
|
||||
def handle_predicate(next, token):
|
||||
token = next()
|
||||
selector = []
|
||||
while token[0] != ']':
|
||||
selector.append( operations[token[0]](next, token) )
|
||||
try:
|
||||
token = next()
|
||||
except StopIteration:
|
||||
break
|
||||
else:
|
||||
if token[0] == "/":
|
||||
token = next()
|
||||
|
||||
if not token[0] and token[1] == 'and':
|
||||
return logical_and(selector, handle_predicate(next, token))
|
||||
|
||||
def select(result):
|
||||
for node in result:
|
||||
subresult = iter((node,))
|
||||
for select in selector:
|
||||
subresult = select(subresult)
|
||||
predicate_result = _get_first_or_none(subresult)
|
||||
if predicate_result is not None:
|
||||
yield node
|
||||
return select
|
||||
|
||||
def logical_and(lhs_selects, rhs_select):
|
||||
def select(result):
|
||||
for node in result:
|
||||
subresult = iter((node,))
|
||||
for select in lhs_selects:
|
||||
subresult = select(subresult)
|
||||
predicate_result = _get_first_or_none(subresult)
|
||||
subresult = iter((node,))
|
||||
if predicate_result is not None:
|
||||
for result_node in rhs_select(subresult):
|
||||
yield node
|
||||
return select
|
||||
|
||||
|
||||
operations = {
|
||||
"@": handle_attribute,
|
||||
"": handle_name,
|
||||
"*": handle_star,
|
||||
".": handle_dot,
|
||||
"//": handle_descendants,
|
||||
"[": handle_predicate,
|
||||
}
|
||||
|
||||
functions = {
|
||||
'not' : handle_func_not
|
||||
}
|
||||
|
||||
def _build_path_iterator(path):
|
||||
# parse pattern
|
||||
stream = iter([ (special,text)
|
||||
for (special,text) in path_tokenizer(path)
|
||||
if special or text ])
|
||||
try:
|
||||
_next = stream.next
|
||||
except AttributeError:
|
||||
# Python 3
|
||||
def _next():
|
||||
return next(stream)
|
||||
token = _next()
|
||||
selector = []
|
||||
while 1:
|
||||
try:
|
||||
selector.append(operations[token[0]](_next, token))
|
||||
except StopIteration:
|
||||
raise ValueError("invalid path")
|
||||
try:
|
||||
token = _next()
|
||||
if token[0] == "/":
|
||||
token = _next()
|
||||
except StopIteration:
|
||||
break
|
||||
return selector
|
||||
|
||||
# main module API
|
||||
|
||||
def iterfind(node, path):
|
||||
selector_chain = _build_path_iterator(path)
|
||||
result = iter((node,))
|
||||
for select in selector_chain:
|
||||
result = select(result)
|
||||
return result
|
||||
|
||||
def find_first(node, path):
|
||||
return _get_first_or_none(iterfind(node, path))
|
||||
|
||||
def find_all(node, path):
|
||||
return list(iterfind(node, path))
|
||||
@@ -0,0 +1,591 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .Errors import error, message
|
||||
from . import ExprNodes
|
||||
from . import Nodes
|
||||
from . import Builtin
|
||||
from . import PyrexTypes
|
||||
from .. import Utils
|
||||
from .PyrexTypes import py_object_type, unspecified_type
|
||||
from .Visitor import CythonTransform, EnvTransform
|
||||
|
||||
try:
|
||||
reduce
|
||||
except NameError:
|
||||
from functools import reduce
|
||||
|
||||
|
||||
class TypedExprNode(ExprNodes.ExprNode):
|
||||
# Used for declaring assignments of a specified type without a known entry.
|
||||
subexprs = []
|
||||
|
||||
def __init__(self, type, pos=None):
|
||||
super(TypedExprNode, self).__init__(pos, type=type)
|
||||
|
||||
object_expr = TypedExprNode(py_object_type)
|
||||
|
||||
|
||||
class MarkParallelAssignments(EnvTransform):
|
||||
# Collects assignments inside parallel blocks prange, with parallel.
|
||||
# Perhaps it's better to move it to ControlFlowAnalysis.
|
||||
|
||||
# tells us whether we're in a normal loop
|
||||
in_loop = False
|
||||
|
||||
parallel_errors = False
|
||||
|
||||
def __init__(self, context):
|
||||
# Track the parallel block scopes (with parallel, for i in prange())
|
||||
self.parallel_block_stack = []
|
||||
super(MarkParallelAssignments, self).__init__(context)
|
||||
|
||||
def mark_assignment(self, lhs, rhs, inplace_op=None):
|
||||
if isinstance(lhs, (ExprNodes.NameNode, Nodes.PyArgDeclNode)):
|
||||
if lhs.entry is None:
|
||||
# TODO: This shouldn't happen...
|
||||
return
|
||||
|
||||
if self.parallel_block_stack:
|
||||
parallel_node = self.parallel_block_stack[-1]
|
||||
previous_assignment = parallel_node.assignments.get(lhs.entry)
|
||||
|
||||
# If there was a previous assignment to the variable, keep the
|
||||
# previous assignment position
|
||||
if previous_assignment:
|
||||
pos, previous_inplace_op = previous_assignment
|
||||
|
||||
if (inplace_op and previous_inplace_op and
|
||||
inplace_op != previous_inplace_op):
|
||||
# x += y; x *= y
|
||||
t = (inplace_op, previous_inplace_op)
|
||||
error(lhs.pos,
|
||||
"Reduction operator '%s' is inconsistent "
|
||||
"with previous reduction operator '%s'" % t)
|
||||
else:
|
||||
pos = lhs.pos
|
||||
|
||||
parallel_node.assignments[lhs.entry] = (pos, inplace_op)
|
||||
parallel_node.assigned_nodes.append(lhs)
|
||||
|
||||
elif isinstance(lhs, ExprNodes.SequenceNode):
|
||||
for i, arg in enumerate(lhs.args):
|
||||
if not rhs or arg.is_starred:
|
||||
item_node = None
|
||||
else:
|
||||
item_node = rhs.inferable_item_node(i)
|
||||
self.mark_assignment(arg, item_node)
|
||||
else:
|
||||
# Could use this info to infer cdef class attributes...
|
||||
pass
|
||||
|
||||
def visit_WithTargetAssignmentStatNode(self, node):
|
||||
self.mark_assignment(node.lhs, node.with_node.enter_call)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_SingleAssignmentNode(self, node):
|
||||
self.mark_assignment(node.lhs, node.rhs)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_CascadedAssignmentNode(self, node):
|
||||
for lhs in node.lhs_list:
|
||||
self.mark_assignment(lhs, node.rhs)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_InPlaceAssignmentNode(self, node):
|
||||
self.mark_assignment(node.lhs, node.create_binop_node(), node.operator)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_ForInStatNode(self, node):
|
||||
# TODO: Remove redundancy with range optimization...
|
||||
is_special = False
|
||||
sequence = node.iterator.sequence
|
||||
target = node.target
|
||||
if isinstance(sequence, ExprNodes.SimpleCallNode):
|
||||
function = sequence.function
|
||||
if sequence.self is None and function.is_name:
|
||||
entry = self.current_env().lookup(function.name)
|
||||
if not entry or entry.is_builtin:
|
||||
if function.name == 'reversed' and len(sequence.args) == 1:
|
||||
sequence = sequence.args[0]
|
||||
elif function.name == 'enumerate' and len(sequence.args) == 1:
|
||||
if target.is_sequence_constructor and len(target.args) == 2:
|
||||
iterator = sequence.args[0]
|
||||
if iterator.is_name:
|
||||
iterator_type = iterator.infer_type(self.current_env())
|
||||
if iterator_type.is_builtin_type:
|
||||
# assume that builtin types have a length within Py_ssize_t
|
||||
self.mark_assignment(
|
||||
target.args[0],
|
||||
ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
|
||||
type=PyrexTypes.c_py_ssize_t_type))
|
||||
target = target.args[1]
|
||||
sequence = sequence.args[0]
|
||||
if isinstance(sequence, ExprNodes.SimpleCallNode):
|
||||
function = sequence.function
|
||||
if sequence.self is None and function.is_name:
|
||||
entry = self.current_env().lookup(function.name)
|
||||
if not entry or entry.is_builtin:
|
||||
if function.name in ('range', 'xrange'):
|
||||
is_special = True
|
||||
for arg in sequence.args[:2]:
|
||||
self.mark_assignment(target, arg)
|
||||
if len(sequence.args) > 2:
|
||||
self.mark_assignment(
|
||||
target,
|
||||
ExprNodes.binop_node(node.pos,
|
||||
'+',
|
||||
sequence.args[0],
|
||||
sequence.args[2]))
|
||||
|
||||
if not is_special:
|
||||
# A for-loop basically translates to subsequent calls to
|
||||
# __getitem__(), so using an IndexNode here allows us to
|
||||
# naturally infer the base type of pointers, C arrays,
|
||||
# Python strings, etc., while correctly falling back to an
|
||||
# object type when the base type cannot be handled.
|
||||
self.mark_assignment(target, ExprNodes.IndexNode(
|
||||
node.pos,
|
||||
base=sequence,
|
||||
index=ExprNodes.IntNode(target.pos, value='PY_SSIZE_T_MAX',
|
||||
type=PyrexTypes.c_py_ssize_t_type)))
|
||||
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_ForFromStatNode(self, node):
|
||||
self.mark_assignment(node.target, node.bound1)
|
||||
if node.step is not None:
|
||||
self.mark_assignment(node.target,
|
||||
ExprNodes.binop_node(node.pos,
|
||||
'+',
|
||||
node.bound1,
|
||||
node.step))
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_WhileStatNode(self, node):
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_ExceptClauseNode(self, node):
|
||||
if node.target is not None:
|
||||
self.mark_assignment(node.target, object_expr)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_FromCImportStatNode(self, node):
|
||||
pass # Can't be assigned to...
|
||||
|
||||
def visit_FromImportStatNode(self, node):
|
||||
for name, target in node.items:
|
||||
if name != "*":
|
||||
self.mark_assignment(target, object_expr)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_DefNode(self, node):
|
||||
# use fake expressions with the right result type
|
||||
if node.star_arg:
|
||||
self.mark_assignment(
|
||||
node.star_arg, TypedExprNode(Builtin.tuple_type, node.pos))
|
||||
if node.starstar_arg:
|
||||
self.mark_assignment(
|
||||
node.starstar_arg, TypedExprNode(Builtin.dict_type, node.pos))
|
||||
EnvTransform.visit_FuncDefNode(self, node)
|
||||
return node
|
||||
|
||||
def visit_DelStatNode(self, node):
|
||||
for arg in node.args:
|
||||
self.mark_assignment(arg, arg)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_ParallelStatNode(self, node):
|
||||
if self.parallel_block_stack:
|
||||
node.parent = self.parallel_block_stack[-1]
|
||||
else:
|
||||
node.parent = None
|
||||
|
||||
nested = False
|
||||
if node.is_prange:
|
||||
if not node.parent:
|
||||
node.is_parallel = True
|
||||
else:
|
||||
node.is_parallel = (node.parent.is_prange or not
|
||||
node.parent.is_parallel)
|
||||
nested = node.parent.is_prange
|
||||
else:
|
||||
node.is_parallel = True
|
||||
# Note: nested with parallel() blocks are handled by
|
||||
# ParallelRangeTransform!
|
||||
# nested = node.parent
|
||||
nested = node.parent and node.parent.is_prange
|
||||
|
||||
self.parallel_block_stack.append(node)
|
||||
|
||||
nested = nested or len(self.parallel_block_stack) > 2
|
||||
if not self.parallel_errors and nested and not node.is_prange:
|
||||
error(node.pos, "Only prange() may be nested")
|
||||
self.parallel_errors = True
|
||||
|
||||
if node.is_prange:
|
||||
child_attrs = node.child_attrs
|
||||
node.child_attrs = ['body', 'target', 'args']
|
||||
self.visitchildren(node)
|
||||
node.child_attrs = child_attrs
|
||||
|
||||
self.parallel_block_stack.pop()
|
||||
if node.else_clause:
|
||||
node.else_clause = self.visit(node.else_clause)
|
||||
else:
|
||||
self.visitchildren(node)
|
||||
self.parallel_block_stack.pop()
|
||||
|
||||
self.parallel_errors = False
|
||||
return node
|
||||
|
||||
def visit_YieldExprNode(self, node):
|
||||
if self.parallel_block_stack:
|
||||
error(node.pos, "'%s' not allowed in parallel sections" % node.expr_keyword)
|
||||
return node
|
||||
|
||||
def visit_ReturnStatNode(self, node):
|
||||
node.in_parallel = bool(self.parallel_block_stack)
|
||||
return node
|
||||
|
||||
|
||||
class MarkOverflowingArithmetic(CythonTransform):
|
||||
|
||||
# It may be possible to integrate this with the above for
|
||||
# performance improvements (though likely not worth it).
|
||||
|
||||
might_overflow = False
|
||||
|
||||
def __call__(self, root):
|
||||
self.env_stack = []
|
||||
self.env = root.scope
|
||||
return super(MarkOverflowingArithmetic, self).__call__(root)
|
||||
|
||||
def visit_safe_node(self, node):
|
||||
self.might_overflow, saved = False, self.might_overflow
|
||||
self.visitchildren(node)
|
||||
self.might_overflow = saved
|
||||
return node
|
||||
|
||||
def visit_neutral_node(self, node):
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_dangerous_node(self, node):
|
||||
self.might_overflow, saved = True, self.might_overflow
|
||||
self.visitchildren(node)
|
||||
self.might_overflow = saved
|
||||
return node
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
self.env_stack.append(self.env)
|
||||
self.env = node.local_scope
|
||||
self.visit_safe_node(node)
|
||||
self.env = self.env_stack.pop()
|
||||
return node
|
||||
|
||||
def visit_NameNode(self, node):
|
||||
if self.might_overflow:
|
||||
entry = node.entry or self.env.lookup(node.name)
|
||||
if entry:
|
||||
entry.might_overflow = True
|
||||
return node
|
||||
|
||||
def visit_BinopNode(self, node):
|
||||
if node.operator in '&|^':
|
||||
return self.visit_neutral_node(node)
|
||||
else:
|
||||
return self.visit_dangerous_node(node)
|
||||
|
||||
def visit_SimpleCallNode(self, node):
|
||||
if node.function.is_name and node.function.name == 'abs':
|
||||
# Overflows for minimum value of fixed size ints.
|
||||
return self.visit_dangerous_node(node)
|
||||
else:
|
||||
return self.visit_neutral_node(node)
|
||||
|
||||
visit_UnopNode = visit_neutral_node
|
||||
|
||||
visit_UnaryMinusNode = visit_dangerous_node
|
||||
|
||||
visit_InPlaceAssignmentNode = visit_dangerous_node
|
||||
|
||||
visit_Node = visit_safe_node
|
||||
|
||||
def visit_assignment(self, lhs, rhs):
|
||||
if (isinstance(rhs, ExprNodes.IntNode)
|
||||
and isinstance(lhs, ExprNodes.NameNode)
|
||||
and Utils.long_literal(rhs.value)):
|
||||
entry = lhs.entry or self.env.lookup(lhs.name)
|
||||
if entry:
|
||||
entry.might_overflow = True
|
||||
|
||||
def visit_SingleAssignmentNode(self, node):
|
||||
self.visit_assignment(node.lhs, node.rhs)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
def visit_CascadedAssignmentNode(self, node):
|
||||
for lhs in node.lhs_list:
|
||||
self.visit_assignment(lhs, node.rhs)
|
||||
self.visitchildren(node)
|
||||
return node
|
||||
|
||||
class PyObjectTypeInferer(object):
|
||||
"""
|
||||
If it's not declared, it's a PyObject.
|
||||
"""
|
||||
def infer_types(self, scope):
|
||||
"""
|
||||
Given a dict of entries, map all unspecified types to a specified type.
|
||||
"""
|
||||
for name, entry in scope.entries.items():
|
||||
if entry.type is unspecified_type:
|
||||
entry.type = py_object_type
|
||||
|
||||
class SimpleAssignmentTypeInferer(object):
|
||||
"""
|
||||
Very basic type inference.
|
||||
|
||||
Note: in order to support cross-closure type inference, this must be
|
||||
applies to nested scopes in top-down order.
|
||||
"""
|
||||
def set_entry_type(self, entry, entry_type):
|
||||
entry.type = entry_type
|
||||
for e in entry.all_entries():
|
||||
e.type = entry_type
|
||||
|
||||
def infer_types(self, scope):
|
||||
enabled = scope.directives['infer_types']
|
||||
verbose = scope.directives['infer_types.verbose']
|
||||
|
||||
if enabled == True:
|
||||
spanning_type = aggressive_spanning_type
|
||||
elif enabled is None: # safe mode
|
||||
spanning_type = safe_spanning_type
|
||||
else:
|
||||
for entry in scope.entries.values():
|
||||
if entry.type is unspecified_type:
|
||||
self.set_entry_type(entry, py_object_type)
|
||||
return
|
||||
|
||||
# Set of assignments
|
||||
assignments = set()
|
||||
assmts_resolved = set()
|
||||
dependencies = {}
|
||||
assmt_to_names = {}
|
||||
|
||||
for name, entry in scope.entries.items():
|
||||
for assmt in entry.cf_assignments:
|
||||
names = assmt.type_dependencies()
|
||||
assmt_to_names[assmt] = names
|
||||
assmts = set()
|
||||
for node in names:
|
||||
assmts.update(node.cf_state)
|
||||
dependencies[assmt] = assmts
|
||||
if entry.type is unspecified_type:
|
||||
assignments.update(entry.cf_assignments)
|
||||
else:
|
||||
assmts_resolved.update(entry.cf_assignments)
|
||||
|
||||
def infer_name_node_type(node):
|
||||
types = [assmt.inferred_type for assmt in node.cf_state]
|
||||
if not types:
|
||||
node_type = py_object_type
|
||||
else:
|
||||
entry = node.entry
|
||||
node_type = spanning_type(
|
||||
types, entry.might_overflow, entry.pos, scope)
|
||||
node.inferred_type = node_type
|
||||
|
||||
def infer_name_node_type_partial(node):
|
||||
types = [assmt.inferred_type for assmt in node.cf_state
|
||||
if assmt.inferred_type is not None]
|
||||
if not types:
|
||||
return
|
||||
entry = node.entry
|
||||
return spanning_type(types, entry.might_overflow, entry.pos, scope)
|
||||
|
||||
def inferred_types(entry):
|
||||
has_none = False
|
||||
has_pyobjects = False
|
||||
types = []
|
||||
for assmt in entry.cf_assignments:
|
||||
if assmt.rhs.is_none:
|
||||
has_none = True
|
||||
else:
|
||||
rhs_type = assmt.inferred_type
|
||||
if rhs_type and rhs_type.is_pyobject:
|
||||
has_pyobjects = True
|
||||
types.append(rhs_type)
|
||||
# Ignore None assignments as long as there are concrete Python type assignments.
|
||||
# but include them if None is the only assigned Python object.
|
||||
if has_none and not has_pyobjects:
|
||||
types.append(py_object_type)
|
||||
return types
|
||||
|
||||
def resolve_assignments(assignments):
|
||||
resolved = set()
|
||||
for assmt in assignments:
|
||||
deps = dependencies[assmt]
|
||||
# All assignments are resolved
|
||||
if assmts_resolved.issuperset(deps):
|
||||
for node in assmt_to_names[assmt]:
|
||||
infer_name_node_type(node)
|
||||
# Resolve assmt
|
||||
inferred_type = assmt.infer_type()
|
||||
assmts_resolved.add(assmt)
|
||||
resolved.add(assmt)
|
||||
assignments.difference_update(resolved)
|
||||
return resolved
|
||||
|
||||
def partial_infer(assmt):
|
||||
partial_types = []
|
||||
for node in assmt_to_names[assmt]:
|
||||
partial_type = infer_name_node_type_partial(node)
|
||||
if partial_type is None:
|
||||
return False
|
||||
partial_types.append((node, partial_type))
|
||||
for node, partial_type in partial_types:
|
||||
node.inferred_type = partial_type
|
||||
assmt.infer_type()
|
||||
return True
|
||||
|
||||
partial_assmts = set()
|
||||
def resolve_partial(assignments):
|
||||
# try to handle circular references
|
||||
partials = set()
|
||||
for assmt in assignments:
|
||||
if assmt in partial_assmts:
|
||||
continue
|
||||
if partial_infer(assmt):
|
||||
partials.add(assmt)
|
||||
assmts_resolved.add(assmt)
|
||||
partial_assmts.update(partials)
|
||||
return partials
|
||||
|
||||
# Infer assignments
|
||||
while True:
|
||||
if not resolve_assignments(assignments):
|
||||
if not resolve_partial(assignments):
|
||||
break
|
||||
inferred = set()
|
||||
# First pass
|
||||
for entry in scope.entries.values():
|
||||
if entry.type is not unspecified_type:
|
||||
continue
|
||||
entry_type = py_object_type
|
||||
if assmts_resolved.issuperset(entry.cf_assignments):
|
||||
types = inferred_types(entry)
|
||||
if types and all(types):
|
||||
entry_type = spanning_type(
|
||||
types, entry.might_overflow, entry.pos, scope)
|
||||
inferred.add(entry)
|
||||
self.set_entry_type(entry, entry_type)
|
||||
|
||||
def reinfer():
|
||||
dirty = False
|
||||
for entry in inferred:
|
||||
for assmt in entry.cf_assignments:
|
||||
assmt.infer_type()
|
||||
types = inferred_types(entry)
|
||||
new_type = spanning_type(types, entry.might_overflow, entry.pos, scope)
|
||||
if new_type != entry.type:
|
||||
self.set_entry_type(entry, new_type)
|
||||
dirty = True
|
||||
return dirty
|
||||
|
||||
# types propagation
|
||||
while reinfer():
|
||||
pass
|
||||
|
||||
if verbose:
|
||||
for entry in inferred:
|
||||
message(entry.pos, "inferred '%s' to be of type '%s'" % (
|
||||
entry.name, entry.type))
|
||||
|
||||
|
||||
def find_spanning_type(type1, type2):
|
||||
if type1 is type2:
|
||||
result_type = type1
|
||||
elif type1 is PyrexTypes.c_bint_type or type2 is PyrexTypes.c_bint_type:
|
||||
# type inference can break the coercion back to a Python bool
|
||||
# if it returns an arbitrary int type here
|
||||
return py_object_type
|
||||
else:
|
||||
result_type = PyrexTypes.spanning_type(type1, type2)
|
||||
if result_type in (PyrexTypes.c_double_type, PyrexTypes.c_float_type,
|
||||
Builtin.float_type):
|
||||
# Python's float type is just a C double, so it's safe to
|
||||
# use the C type instead
|
||||
return PyrexTypes.c_double_type
|
||||
return result_type
|
||||
|
||||
def simply_type(result_type, pos):
|
||||
if result_type.is_reference:
|
||||
result_type = result_type.ref_base_type
|
||||
if result_type.is_const:
|
||||
result_type = result_type.const_base_type
|
||||
if result_type.is_cpp_class:
|
||||
result_type.check_nullary_constructor(pos)
|
||||
if result_type.is_array:
|
||||
result_type = PyrexTypes.c_ptr_type(result_type.base_type)
|
||||
return result_type
|
||||
|
||||
def aggressive_spanning_type(types, might_overflow, pos, scope):
|
||||
return simply_type(reduce(find_spanning_type, types), pos)
|
||||
|
||||
def safe_spanning_type(types, might_overflow, pos, scope):
|
||||
result_type = simply_type(reduce(find_spanning_type, types), pos)
|
||||
if result_type.is_pyobject:
|
||||
# In theory, any specific Python type is always safe to
|
||||
# infer. However, inferring str can cause some existing code
|
||||
# to break, since we are also now much more strict about
|
||||
# coercion from str to char *. See trac #553.
|
||||
if result_type.name == 'str':
|
||||
return py_object_type
|
||||
else:
|
||||
return result_type
|
||||
elif result_type is PyrexTypes.c_double_type:
|
||||
# Python's float type is just a C double, so it's safe to use
|
||||
# the C type instead
|
||||
return result_type
|
||||
elif result_type is PyrexTypes.c_bint_type:
|
||||
# find_spanning_type() only returns 'bint' for clean boolean
|
||||
# operations without other int types, so this is safe, too
|
||||
return result_type
|
||||
elif result_type.is_pythran_expr:
|
||||
return result_type
|
||||
elif result_type.is_ptr:
|
||||
# Any pointer except (signed|unsigned|) char* can't implicitly
|
||||
# become a PyObject, and inferring char* is now accepted, too.
|
||||
return result_type
|
||||
elif result_type.is_cpp_class:
|
||||
# These can't implicitly become Python objects either.
|
||||
return result_type
|
||||
elif result_type.is_struct:
|
||||
# Though we have struct -> object for some structs, this is uncommonly
|
||||
# used, won't arise in pure Python, and there shouldn't be side
|
||||
# effects, so I'm declaring this safe.
|
||||
return result_type
|
||||
# TODO: double complex should be OK as well, but we need
|
||||
# to make sure everything is supported.
|
||||
elif (result_type.is_int or result_type.is_enum) and not might_overflow:
|
||||
return result_type
|
||||
elif (not result_type.can_coerce_to_pyobject(scope)
|
||||
and not result_type.is_error):
|
||||
return result_type
|
||||
return py_object_type
|
||||
|
||||
|
||||
def get_type_inferer():
|
||||
return SimpleAssignmentTypeInferer()
|
||||
@@ -0,0 +1,941 @@
|
||||
#
|
||||
# Tables describing slots in the CPython type object
|
||||
# and associated know-how.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import Naming
|
||||
from . import PyrexTypes
|
||||
from .Errors import error
|
||||
|
||||
invisible = ['__cinit__', '__dealloc__', '__richcmp__',
|
||||
'__nonzero__', '__bool__']
|
||||
|
||||
richcmp_special_methods = ['__eq__', '__ne__', '__lt__', '__gt__', '__le__', '__ge__']
|
||||
|
||||
|
||||
class Signature(object):
|
||||
# Method slot signature descriptor.
|
||||
#
|
||||
# has_dummy_arg boolean
|
||||
# has_generic_args boolean
|
||||
# fixed_arg_format string
|
||||
# ret_format string
|
||||
# error_value string
|
||||
#
|
||||
# The formats are strings made up of the following
|
||||
# characters:
|
||||
#
|
||||
# 'O' Python object
|
||||
# 'T' Python object of the type of 'self'
|
||||
# 'v' void
|
||||
# 'p' void *
|
||||
# 'P' void **
|
||||
# 'i' int
|
||||
# 'b' bint
|
||||
# 'I' int *
|
||||
# 'l' long
|
||||
# 'f' float
|
||||
# 'd' double
|
||||
# 'h' Py_hash_t
|
||||
# 'z' Py_ssize_t
|
||||
# 'Z' Py_ssize_t *
|
||||
# 's' char *
|
||||
# 'S' char **
|
||||
# 'r' int used only to signal exception
|
||||
# 'B' Py_buffer *
|
||||
# '-' dummy 'self' argument (not used)
|
||||
# '*' rest of args passed as generic Python
|
||||
# arg tuple and kw dict (must be last
|
||||
# char in format string)
|
||||
|
||||
format_map = {
|
||||
'O': PyrexTypes.py_object_type,
|
||||
'v': PyrexTypes.c_void_type,
|
||||
'p': PyrexTypes.c_void_ptr_type,
|
||||
'P': PyrexTypes.c_void_ptr_ptr_type,
|
||||
'i': PyrexTypes.c_int_type,
|
||||
'b': PyrexTypes.c_bint_type,
|
||||
'I': PyrexTypes.c_int_ptr_type,
|
||||
'l': PyrexTypes.c_long_type,
|
||||
'f': PyrexTypes.c_float_type,
|
||||
'd': PyrexTypes.c_double_type,
|
||||
'h': PyrexTypes.c_py_hash_t_type,
|
||||
'z': PyrexTypes.c_py_ssize_t_type,
|
||||
'Z': PyrexTypes.c_py_ssize_t_ptr_type,
|
||||
's': PyrexTypes.c_char_ptr_type,
|
||||
'S': PyrexTypes.c_char_ptr_ptr_type,
|
||||
'r': PyrexTypes.c_returncode_type,
|
||||
'B': PyrexTypes.c_py_buffer_ptr_type,
|
||||
# 'T', '-' and '*' are handled otherwise
|
||||
# and are not looked up in here
|
||||
}
|
||||
|
||||
type_to_format_map = dict(
|
||||
(type_, format_) for format_, type_ in format_map.items())
|
||||
|
||||
error_value_map = {
|
||||
'O': "NULL",
|
||||
'T': "NULL",
|
||||
'i': "-1",
|
||||
'b': "-1",
|
||||
'l': "-1",
|
||||
'r': "-1",
|
||||
'h': "-1",
|
||||
'z': "-1",
|
||||
}
|
||||
|
||||
def __init__(self, arg_format, ret_format):
|
||||
self.has_dummy_arg = 0
|
||||
self.has_generic_args = 0
|
||||
if arg_format[:1] == '-':
|
||||
self.has_dummy_arg = 1
|
||||
arg_format = arg_format[1:]
|
||||
if arg_format[-1:] == '*':
|
||||
self.has_generic_args = 1
|
||||
arg_format = arg_format[:-1]
|
||||
self.fixed_arg_format = arg_format
|
||||
self.ret_format = ret_format
|
||||
self.error_value = self.error_value_map.get(ret_format, None)
|
||||
self.exception_check = ret_format != 'r' and self.error_value is not None
|
||||
self.is_staticmethod = False
|
||||
|
||||
def __repr__(self):
|
||||
return '<Signature[%s(%s%s)]>' % (
|
||||
self.ret_format,
|
||||
', '.join(self.fixed_arg_format),
|
||||
'*' if self.has_generic_args else '')
|
||||
|
||||
def num_fixed_args(self):
|
||||
return len(self.fixed_arg_format)
|
||||
|
||||
def is_self_arg(self, i):
|
||||
# argument is 'self' for methods or 'class' for classmethods
|
||||
return self.fixed_arg_format[i] == 'T'
|
||||
|
||||
def returns_self_type(self):
|
||||
# return type is same as 'self' argument type
|
||||
return self.ret_format == 'T'
|
||||
|
||||
def fixed_arg_type(self, i):
|
||||
return self.format_map[self.fixed_arg_format[i]]
|
||||
|
||||
def return_type(self):
|
||||
return self.format_map[self.ret_format]
|
||||
|
||||
def format_from_type(self, arg_type):
|
||||
if arg_type.is_pyobject:
|
||||
arg_type = PyrexTypes.py_object_type
|
||||
return self.type_to_format_map[arg_type]
|
||||
|
||||
def exception_value(self):
|
||||
return self.error_value_map.get(self.ret_format)
|
||||
|
||||
def function_type(self, self_arg_override=None):
|
||||
# Construct a C function type descriptor for this signature
|
||||
args = []
|
||||
for i in range(self.num_fixed_args()):
|
||||
if self_arg_override is not None and self.is_self_arg(i):
|
||||
assert isinstance(self_arg_override, PyrexTypes.CFuncTypeArg)
|
||||
args.append(self_arg_override)
|
||||
else:
|
||||
arg_type = self.fixed_arg_type(i)
|
||||
args.append(PyrexTypes.CFuncTypeArg("", arg_type, None))
|
||||
if self_arg_override is not None and self.returns_self_type():
|
||||
ret_type = self_arg_override.type
|
||||
else:
|
||||
ret_type = self.return_type()
|
||||
exc_value = self.exception_value()
|
||||
return PyrexTypes.CFuncType(
|
||||
ret_type, args, exception_value=exc_value,
|
||||
exception_check=self.exception_check)
|
||||
|
||||
def method_flags(self):
|
||||
if self.ret_format == "O":
|
||||
full_args = self.fixed_arg_format
|
||||
if self.has_dummy_arg:
|
||||
full_args = "O" + full_args
|
||||
if full_args in ["O", "T"]:
|
||||
if self.has_generic_args:
|
||||
return [method_varargs, method_keywords]
|
||||
else:
|
||||
return [method_noargs]
|
||||
elif full_args in ["OO", "TO"] and not self.has_generic_args:
|
||||
return [method_onearg]
|
||||
|
||||
if self.is_staticmethod:
|
||||
return [method_varargs, method_keywords]
|
||||
return None
|
||||
|
||||
|
||||
class SlotDescriptor(object):
|
||||
# Abstract base class for type slot descriptors.
|
||||
#
|
||||
# slot_name string Member name of the slot in the type object
|
||||
# is_initialised_dynamically Is initialised by code in the module init function
|
||||
# is_inherited Is inherited by subtypes (see PyType_Ready())
|
||||
# py3 Indicates presence of slot in Python 3
|
||||
# py2 Indicates presence of slot in Python 2
|
||||
# ifdef Full #ifdef string that slot is wrapped in. Using this causes py3, py2 and flags to be ignored.)
|
||||
|
||||
def __init__(self, slot_name, dynamic=False, inherited=False,
|
||||
py3=True, py2=True, ifdef=None):
|
||||
self.slot_name = slot_name
|
||||
self.is_initialised_dynamically = dynamic
|
||||
self.is_inherited = inherited
|
||||
self.ifdef = ifdef
|
||||
self.py3 = py3
|
||||
self.py2 = py2
|
||||
|
||||
def preprocessor_guard_code(self):
|
||||
ifdef = self.ifdef
|
||||
py2 = self.py2
|
||||
py3 = self.py3
|
||||
guard = None
|
||||
if ifdef:
|
||||
guard = ("#if %s" % ifdef)
|
||||
elif not py3 or py3 == '<RESERVED>':
|
||||
guard = ("#if PY_MAJOR_VERSION < 3")
|
||||
elif not py2:
|
||||
guard = ("#if PY_MAJOR_VERSION >= 3")
|
||||
return guard
|
||||
|
||||
def generate(self, scope, code):
|
||||
preprocessor_guard = self.preprocessor_guard_code()
|
||||
if preprocessor_guard:
|
||||
code.putln(preprocessor_guard)
|
||||
|
||||
end_pypy_guard = False
|
||||
if self.is_initialised_dynamically:
|
||||
value = "0"
|
||||
else:
|
||||
value = self.slot_code(scope)
|
||||
if value == "0" and self.is_inherited:
|
||||
# PyPy currently has a broken PyType_Ready() that fails to
|
||||
# inherit some slots. To work around this, we explicitly
|
||||
# set inherited slots here, but only in PyPy since CPython
|
||||
# handles this better than we do.
|
||||
inherited_value = value
|
||||
current_scope = scope
|
||||
while (inherited_value == "0"
|
||||
and current_scope.parent_type
|
||||
and current_scope.parent_type.base_type
|
||||
and current_scope.parent_type.base_type.scope):
|
||||
current_scope = current_scope.parent_type.base_type.scope
|
||||
inherited_value = self.slot_code(current_scope)
|
||||
if inherited_value != "0":
|
||||
code.putln("#if CYTHON_COMPILING_IN_PYPY")
|
||||
code.putln("%s, /*%s*/" % (inherited_value, self.slot_name))
|
||||
code.putln("#else")
|
||||
end_pypy_guard = True
|
||||
|
||||
code.putln("%s, /*%s*/" % (value, self.slot_name))
|
||||
|
||||
if end_pypy_guard:
|
||||
code.putln("#endif")
|
||||
|
||||
if self.py3 == '<RESERVED>':
|
||||
code.putln("#else")
|
||||
code.putln("0, /*reserved*/")
|
||||
if preprocessor_guard:
|
||||
code.putln("#endif")
|
||||
|
||||
# Some C implementations have trouble statically
|
||||
# initialising a global with a pointer to an extern
|
||||
# function, so we initialise some of the type slots
|
||||
# in the module init function instead.
|
||||
|
||||
def generate_dynamic_init_code(self, scope, code):
|
||||
if self.is_initialised_dynamically:
|
||||
value = self.slot_code(scope)
|
||||
if value != "0":
|
||||
code.putln("%s.%s = %s;" % (
|
||||
scope.parent_type.typeobj_cname,
|
||||
self.slot_name,
|
||||
value
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class FixedSlot(SlotDescriptor):
|
||||
# Descriptor for a type slot with a fixed value.
|
||||
#
|
||||
# value string
|
||||
|
||||
def __init__(self, slot_name, value, py3=True, py2=True, ifdef=None):
|
||||
SlotDescriptor.__init__(self, slot_name, py3=py3, py2=py2, ifdef=ifdef)
|
||||
self.value = value
|
||||
|
||||
def slot_code(self, scope):
|
||||
return self.value
|
||||
|
||||
|
||||
class EmptySlot(FixedSlot):
|
||||
# Descriptor for a type slot whose value is always 0.
|
||||
|
||||
def __init__(self, slot_name, py3=True, py2=True, ifdef=None):
|
||||
FixedSlot.__init__(self, slot_name, "0", py3=py3, py2=py2, ifdef=ifdef)
|
||||
|
||||
|
||||
class MethodSlot(SlotDescriptor):
|
||||
# Type slot descriptor for a user-definable method.
|
||||
#
|
||||
# signature Signature
|
||||
# method_name string The __xxx__ name of the method
|
||||
# alternatives [string] Alternative list of __xxx__ names for the method
|
||||
|
||||
def __init__(self, signature, slot_name, method_name, fallback=None,
|
||||
py3=True, py2=True, ifdef=None, inherited=True):
|
||||
SlotDescriptor.__init__(self, slot_name, py3=py3, py2=py2,
|
||||
ifdef=ifdef, inherited=inherited)
|
||||
self.signature = signature
|
||||
self.slot_name = slot_name
|
||||
self.method_name = method_name
|
||||
self.alternatives = []
|
||||
method_name_to_slot[method_name] = self
|
||||
#
|
||||
if fallback:
|
||||
self.alternatives.append(fallback)
|
||||
for alt in (self.py2, self.py3):
|
||||
if isinstance(alt, (tuple, list)):
|
||||
slot_name, method_name = alt
|
||||
self.alternatives.append(method_name)
|
||||
method_name_to_slot[method_name] = self
|
||||
|
||||
def slot_code(self, scope):
|
||||
entry = scope.lookup_here(self.method_name)
|
||||
if entry and entry.is_special and entry.func_cname:
|
||||
return entry.func_cname
|
||||
for method_name in self.alternatives:
|
||||
entry = scope.lookup_here(method_name)
|
||||
if entry and entry.is_special and entry.func_cname:
|
||||
return entry.func_cname
|
||||
return "0"
|
||||
|
||||
|
||||
class InternalMethodSlot(SlotDescriptor):
|
||||
# Type slot descriptor for a method which is always
|
||||
# synthesized by Cython.
|
||||
#
|
||||
# slot_name string Member name of the slot in the type object
|
||||
|
||||
def __init__(self, slot_name, **kargs):
|
||||
SlotDescriptor.__init__(self, slot_name, **kargs)
|
||||
|
||||
def slot_code(self, scope):
|
||||
return scope.mangle_internal(self.slot_name)
|
||||
|
||||
|
||||
class GCDependentSlot(InternalMethodSlot):
|
||||
# Descriptor for a slot whose value depends on whether
|
||||
# the type participates in GC.
|
||||
|
||||
def __init__(self, slot_name, **kargs):
|
||||
InternalMethodSlot.__init__(self, slot_name, **kargs)
|
||||
|
||||
def slot_code(self, scope):
|
||||
if not scope.needs_gc():
|
||||
return "0"
|
||||
if not scope.has_cyclic_pyobject_attrs:
|
||||
# if the type does not have GC relevant object attributes, it can
|
||||
# delegate GC methods to its parent - iff the parent functions
|
||||
# are defined in the same module
|
||||
parent_type_scope = scope.parent_type.base_type.scope
|
||||
if scope.parent_scope is parent_type_scope.parent_scope:
|
||||
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
|
||||
if entry.visibility != 'extern':
|
||||
return self.slot_code(parent_type_scope)
|
||||
return InternalMethodSlot.slot_code(self, scope)
|
||||
|
||||
|
||||
class GCClearReferencesSlot(GCDependentSlot):
|
||||
|
||||
def slot_code(self, scope):
|
||||
if scope.needs_tp_clear():
|
||||
return GCDependentSlot.slot_code(self, scope)
|
||||
return "0"
|
||||
|
||||
|
||||
class ConstructorSlot(InternalMethodSlot):
|
||||
# Descriptor for tp_new and tp_dealloc.
|
||||
|
||||
def __init__(self, slot_name, method, **kargs):
|
||||
InternalMethodSlot.__init__(self, slot_name, **kargs)
|
||||
self.method = method
|
||||
|
||||
def slot_code(self, scope):
|
||||
entry = scope.lookup_here(self.method)
|
||||
if (self.slot_name != 'tp_new'
|
||||
and scope.parent_type.base_type
|
||||
and not scope.has_pyobject_attrs
|
||||
and not scope.has_memoryview_attrs
|
||||
and not scope.has_cpp_class_attrs
|
||||
and not (entry and entry.is_special)):
|
||||
# if the type does not have object attributes, it can
|
||||
# delegate GC methods to its parent - iff the parent
|
||||
# functions are defined in the same module
|
||||
parent_type_scope = scope.parent_type.base_type.scope
|
||||
if scope.parent_scope is parent_type_scope.parent_scope:
|
||||
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
|
||||
if entry.visibility != 'extern':
|
||||
return self.slot_code(parent_type_scope)
|
||||
if entry and not entry.is_special:
|
||||
return "0"
|
||||
return InternalMethodSlot.slot_code(self, scope)
|
||||
|
||||
|
||||
class SyntheticSlot(InternalMethodSlot):
|
||||
# Type slot descriptor for a synthesized method which
|
||||
# dispatches to one or more user-defined methods depending
|
||||
# on its arguments. If none of the relevant methods are
|
||||
# defined, the method will not be synthesized and an
|
||||
# alternative default value will be placed in the type
|
||||
# slot.
|
||||
|
||||
def __init__(self, slot_name, user_methods, default_value, **kargs):
|
||||
InternalMethodSlot.__init__(self, slot_name, **kargs)
|
||||
self.user_methods = user_methods
|
||||
self.default_value = default_value
|
||||
|
||||
def slot_code(self, scope):
|
||||
if scope.defines_any_special(self.user_methods):
|
||||
return InternalMethodSlot.slot_code(self, scope)
|
||||
else:
|
||||
return self.default_value
|
||||
|
||||
|
||||
class RichcmpSlot(MethodSlot):
|
||||
def slot_code(self, scope):
|
||||
entry = scope.lookup_here(self.method_name)
|
||||
if entry and entry.is_special and entry.func_cname:
|
||||
return entry.func_cname
|
||||
elif scope.defines_any_special(richcmp_special_methods):
|
||||
return scope.mangle_internal(self.slot_name)
|
||||
else:
|
||||
return "0"
|
||||
|
||||
|
||||
class TypeFlagsSlot(SlotDescriptor):
|
||||
# Descriptor for the type flags slot.
|
||||
|
||||
def slot_code(self, scope):
|
||||
value = "Py_TPFLAGS_DEFAULT"
|
||||
if scope.directives['type_version_tag']:
|
||||
# it's not in 'Py_TPFLAGS_DEFAULT' in Py2
|
||||
value += "|Py_TPFLAGS_HAVE_VERSION_TAG"
|
||||
else:
|
||||
# it's enabled in 'Py_TPFLAGS_DEFAULT' in Py3
|
||||
value = "(%s&~Py_TPFLAGS_HAVE_VERSION_TAG)" % value
|
||||
value += "|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER"
|
||||
if not scope.parent_type.is_final_type:
|
||||
value += "|Py_TPFLAGS_BASETYPE"
|
||||
if scope.needs_gc():
|
||||
value += "|Py_TPFLAGS_HAVE_GC"
|
||||
return value
|
||||
|
||||
|
||||
class DocStringSlot(SlotDescriptor):
|
||||
# Descriptor for the docstring slot.
|
||||
|
||||
def slot_code(self, scope):
|
||||
doc = scope.doc
|
||||
if doc is None:
|
||||
return "0"
|
||||
if doc.is_unicode:
|
||||
doc = doc.as_utf8_string()
|
||||
return doc.as_c_string_literal()
|
||||
|
||||
|
||||
class SuiteSlot(SlotDescriptor):
|
||||
# Descriptor for a substructure of the type object.
|
||||
#
|
||||
# sub_slots [SlotDescriptor]
|
||||
|
||||
def __init__(self, sub_slots, slot_type, slot_name, ifdef=None):
|
||||
SlotDescriptor.__init__(self, slot_name, ifdef=ifdef)
|
||||
self.sub_slots = sub_slots
|
||||
self.slot_type = slot_type
|
||||
substructures.append(self)
|
||||
|
||||
def is_empty(self, scope):
|
||||
for slot in self.sub_slots:
|
||||
if slot.slot_code(scope) != "0":
|
||||
return False
|
||||
return True
|
||||
|
||||
def substructure_cname(self, scope):
|
||||
return "%s%s_%s" % (Naming.pyrex_prefix, self.slot_name, scope.class_name)
|
||||
|
||||
def slot_code(self, scope):
|
||||
if not self.is_empty(scope):
|
||||
return "&%s" % self.substructure_cname(scope)
|
||||
return "0"
|
||||
|
||||
def generate_substructure(self, scope, code):
|
||||
if not self.is_empty(scope):
|
||||
code.putln("")
|
||||
if self.ifdef:
|
||||
code.putln("#if %s" % self.ifdef)
|
||||
code.putln(
|
||||
"static %s %s = {" % (
|
||||
self.slot_type,
|
||||
self.substructure_cname(scope)))
|
||||
for slot in self.sub_slots:
|
||||
slot.generate(scope, code)
|
||||
code.putln("};")
|
||||
if self.ifdef:
|
||||
code.putln("#endif")
|
||||
|
||||
substructures = [] # List of all SuiteSlot instances
|
||||
|
||||
class MethodTableSlot(SlotDescriptor):
|
||||
# Slot descriptor for the method table.
|
||||
|
||||
def slot_code(self, scope):
|
||||
if scope.pyfunc_entries:
|
||||
return scope.method_table_cname
|
||||
else:
|
||||
return "0"
|
||||
|
||||
|
||||
class MemberTableSlot(SlotDescriptor):
|
||||
# Slot descriptor for the table of Python-accessible attributes.
|
||||
|
||||
def slot_code(self, scope):
|
||||
return "0"
|
||||
|
||||
|
||||
class GetSetSlot(SlotDescriptor):
|
||||
# Slot descriptor for the table of attribute get & set methods.
|
||||
|
||||
def slot_code(self, scope):
|
||||
if scope.property_entries:
|
||||
return scope.getset_table_cname
|
||||
else:
|
||||
return "0"
|
||||
|
||||
|
||||
class BaseClassSlot(SlotDescriptor):
|
||||
# Slot descriptor for the base class slot.
|
||||
|
||||
def __init__(self, name):
|
||||
SlotDescriptor.__init__(self, name, dynamic = 1)
|
||||
|
||||
def generate_dynamic_init_code(self, scope, code):
|
||||
base_type = scope.parent_type.base_type
|
||||
if base_type:
|
||||
code.putln("%s.%s = %s;" % (
|
||||
scope.parent_type.typeobj_cname,
|
||||
self.slot_name,
|
||||
base_type.typeptr_cname))
|
||||
|
||||
|
||||
class DictOffsetSlot(SlotDescriptor):
|
||||
# Slot descriptor for a class' dict offset, for dynamic attributes.
|
||||
|
||||
def slot_code(self, scope):
|
||||
dict_entry = scope.lookup_here("__dict__") if not scope.is_closure_class_scope else None
|
||||
if dict_entry and dict_entry.is_variable:
|
||||
if getattr(dict_entry.type, 'cname', None) != 'PyDict_Type':
|
||||
error(dict_entry.pos, "__dict__ slot must be of type 'dict'")
|
||||
return "0"
|
||||
type = scope.parent_type
|
||||
if type.typedef_flag:
|
||||
objstruct = type.objstruct_cname
|
||||
else:
|
||||
objstruct = "struct %s" % type.objstruct_cname
|
||||
return ("offsetof(%s, %s)" % (
|
||||
objstruct,
|
||||
dict_entry.cname))
|
||||
else:
|
||||
return "0"
|
||||
|
||||
|
||||
# The following dictionary maps __xxx__ method names to slot descriptors.
|
||||
|
||||
method_name_to_slot = {}
|
||||
|
||||
## The following slots are (or could be) initialised with an
|
||||
## extern function pointer.
|
||||
#
|
||||
#slots_initialised_from_extern = (
|
||||
# "tp_free",
|
||||
#)
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Utility functions for accessing slot table data structures
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
def get_special_method_signature(name):
|
||||
# Given a method name, if it is a special method,
|
||||
# return its signature, else return None.
|
||||
slot = method_name_to_slot.get(name)
|
||||
if slot:
|
||||
return slot.signature
|
||||
elif name in richcmp_special_methods:
|
||||
return ibinaryfunc
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_property_accessor_signature(name):
|
||||
# Return signature of accessor for an extension type
|
||||
# property, else None.
|
||||
return property_accessor_signatures.get(name)
|
||||
|
||||
|
||||
def get_base_slot_function(scope, slot):
|
||||
# Returns the function implementing this slot in the baseclass.
|
||||
# This is useful for enabling the compiler to optimize calls
|
||||
# that recursively climb the class hierarchy.
|
||||
base_type = scope.parent_type.base_type
|
||||
if scope.parent_scope is base_type.scope.parent_scope:
|
||||
parent_slot = slot.slot_code(base_type.scope)
|
||||
if parent_slot != '0':
|
||||
entry = scope.parent_scope.lookup_here(scope.parent_type.base_type.name)
|
||||
if entry.visibility != 'extern':
|
||||
return parent_slot
|
||||
return None
|
||||
|
||||
|
||||
def get_slot_function(scope, slot):
|
||||
# Returns the function implementing this slot in the baseclass.
|
||||
# This is useful for enabling the compiler to optimize calls
|
||||
# that recursively climb the class hierarchy.
|
||||
slot_code = slot.slot_code(scope)
|
||||
if slot_code != '0':
|
||||
entry = scope.parent_scope.lookup_here(scope.parent_type.name)
|
||||
if entry.visibility != 'extern':
|
||||
return slot_code
|
||||
return None
|
||||
|
||||
|
||||
def get_slot_by_name(slot_name):
|
||||
# For now, only search the type struct, no referenced sub-structs.
|
||||
for slot in slot_table:
|
||||
if slot.slot_name == slot_name:
|
||||
return slot
|
||||
assert False, "Slot not found: %s" % slot_name
|
||||
|
||||
|
||||
def get_slot_code_by_name(scope, slot_name):
|
||||
slot = get_slot_by_name(slot_name)
|
||||
return slot.slot_code(scope)
|
||||
|
||||
def is_reverse_number_slot(name):
|
||||
"""
|
||||
Tries to identify __radd__ and friends (so the METH_COEXIST flag can be applied).
|
||||
|
||||
There's no great consequence if it inadvertently identifies a few other methods
|
||||
so just use a simple rule rather than an exact list.
|
||||
"""
|
||||
if name.startswith("__r") and name.endswith("__"):
|
||||
forward_name = name.replace("r", "", 1)
|
||||
for meth in PyNumberMethods:
|
||||
if getattr(meth, "method_name", None) == forward_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Signatures for generic Python functions and methods.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
pyfunction_signature = Signature("-*", "O")
|
||||
pymethod_signature = Signature("T*", "O")
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Signatures for simple Python functions.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
pyfunction_noargs = Signature("-", "O")
|
||||
pyfunction_onearg = Signature("-O", "O")
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Signatures for the various kinds of function that
|
||||
# can appear in the type object and its substructures.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
unaryfunc = Signature("T", "O") # typedef PyObject * (*unaryfunc)(PyObject *);
|
||||
binaryfunc = Signature("OO", "O") # typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
|
||||
ibinaryfunc = Signature("TO", "O") # typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
|
||||
ternaryfunc = Signature("OOO", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
|
||||
iternaryfunc = Signature("TOO", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
|
||||
callfunc = Signature("T*", "O") # typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
|
||||
inquiry = Signature("T", "i") # typedef int (*inquiry)(PyObject *);
|
||||
lenfunc = Signature("T", "z") # typedef Py_ssize_t (*lenfunc)(PyObject *);
|
||||
|
||||
# typedef int (*coercion)(PyObject **, PyObject **);
|
||||
intargfunc = Signature("Ti", "O") # typedef PyObject *(*intargfunc)(PyObject *, int);
|
||||
ssizeargfunc = Signature("Tz", "O") # typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t);
|
||||
intintargfunc = Signature("Tii", "O") # typedef PyObject *(*intintargfunc)(PyObject *, int, int);
|
||||
ssizessizeargfunc = Signature("Tzz", "O") # typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
|
||||
intobjargproc = Signature("TiO", 'r') # typedef int(*intobjargproc)(PyObject *, int, PyObject *);
|
||||
ssizeobjargproc = Signature("TzO", 'r') # typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
|
||||
intintobjargproc = Signature("TiiO", 'r') # typedef int(*intintobjargproc)(PyObject *, int, int, PyObject *);
|
||||
ssizessizeobjargproc = Signature("TzzO", 'r') # typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
|
||||
|
||||
intintargproc = Signature("Tii", 'r')
|
||||
ssizessizeargproc = Signature("Tzz", 'r')
|
||||
objargfunc = Signature("TO", "O")
|
||||
objobjargproc = Signature("TOO", 'r') # typedef int (*objobjargproc)(PyObject *, PyObject *, PyObject *);
|
||||
readbufferproc = Signature("TzP", "z") # typedef Py_ssize_t (*readbufferproc)(PyObject *, Py_ssize_t, void **);
|
||||
writebufferproc = Signature("TzP", "z") # typedef Py_ssize_t (*writebufferproc)(PyObject *, Py_ssize_t, void **);
|
||||
segcountproc = Signature("TZ", "z") # typedef Py_ssize_t (*segcountproc)(PyObject *, Py_ssize_t *);
|
||||
charbufferproc = Signature("TzS", "z") # typedef Py_ssize_t (*charbufferproc)(PyObject *, Py_ssize_t, char **);
|
||||
objargproc = Signature("TO", 'r') # typedef int (*objobjproc)(PyObject *, PyObject *);
|
||||
# typedef int (*visitproc)(PyObject *, void *);
|
||||
# typedef int (*traverseproc)(PyObject *, visitproc, void *);
|
||||
|
||||
destructor = Signature("T", "v") # typedef void (*destructor)(PyObject *);
|
||||
# printfunc = Signature("TFi", 'r') # typedef int (*printfunc)(PyObject *, FILE *, int);
|
||||
# typedef PyObject *(*getattrfunc)(PyObject *, char *);
|
||||
getattrofunc = Signature("TO", "O") # typedef PyObject *(*getattrofunc)(PyObject *, PyObject *);
|
||||
# typedef int (*setattrfunc)(PyObject *, char *, PyObject *);
|
||||
setattrofunc = Signature("TOO", 'r') # typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *);
|
||||
delattrofunc = Signature("TO", 'r')
|
||||
cmpfunc = Signature("TO", "i") # typedef int (*cmpfunc)(PyObject *, PyObject *);
|
||||
reprfunc = Signature("T", "O") # typedef PyObject *(*reprfunc)(PyObject *);
|
||||
hashfunc = Signature("T", "h") # typedef Py_hash_t (*hashfunc)(PyObject *);
|
||||
richcmpfunc = Signature("TOi", "O") # typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);
|
||||
getiterfunc = Signature("T", "O") # typedef PyObject *(*getiterfunc) (PyObject *);
|
||||
iternextfunc = Signature("T", "O") # typedef PyObject *(*iternextfunc) (PyObject *);
|
||||
descrgetfunc = Signature("TOO", "O") # typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *);
|
||||
descrsetfunc = Signature("TOO", 'r') # typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *);
|
||||
descrdelfunc = Signature("TO", 'r')
|
||||
initproc = Signature("T*", 'r') # typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
|
||||
# typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *);
|
||||
# typedef PyObject *(*allocfunc)(struct _typeobject *, int);
|
||||
|
||||
getbufferproc = Signature("TBi", "r") # typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
|
||||
releasebufferproc = Signature("TB", "v") # typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Signatures for accessor methods of properties.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
property_accessor_signatures = {
|
||||
'__get__': Signature("T", "O"),
|
||||
'__set__': Signature("TO", 'r'),
|
||||
'__del__': Signature("T", 'r')
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Descriptor tables for the slots of the various type object
|
||||
# substructures, in the order they appear in the structure.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
PyNumberMethods_Py3_GUARD = "PY_MAJOR_VERSION < 3 || (CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x03050000)"
|
||||
|
||||
PyNumberMethods = (
|
||||
MethodSlot(binaryfunc, "nb_add", "__add__"),
|
||||
MethodSlot(binaryfunc, "nb_subtract", "__sub__"),
|
||||
MethodSlot(binaryfunc, "nb_multiply", "__mul__"),
|
||||
MethodSlot(binaryfunc, "nb_divide", "__div__", ifdef = PyNumberMethods_Py3_GUARD),
|
||||
MethodSlot(binaryfunc, "nb_remainder", "__mod__"),
|
||||
MethodSlot(binaryfunc, "nb_divmod", "__divmod__"),
|
||||
MethodSlot(ternaryfunc, "nb_power", "__pow__"),
|
||||
MethodSlot(unaryfunc, "nb_negative", "__neg__"),
|
||||
MethodSlot(unaryfunc, "nb_positive", "__pos__"),
|
||||
MethodSlot(unaryfunc, "nb_absolute", "__abs__"),
|
||||
MethodSlot(inquiry, "nb_nonzero", "__nonzero__", py3 = ("nb_bool", "__bool__")),
|
||||
MethodSlot(unaryfunc, "nb_invert", "__invert__"),
|
||||
MethodSlot(binaryfunc, "nb_lshift", "__lshift__"),
|
||||
MethodSlot(binaryfunc, "nb_rshift", "__rshift__"),
|
||||
MethodSlot(binaryfunc, "nb_and", "__and__"),
|
||||
MethodSlot(binaryfunc, "nb_xor", "__xor__"),
|
||||
MethodSlot(binaryfunc, "nb_or", "__or__"),
|
||||
EmptySlot("nb_coerce", ifdef = PyNumberMethods_Py3_GUARD),
|
||||
MethodSlot(unaryfunc, "nb_int", "__int__", fallback="__long__"),
|
||||
MethodSlot(unaryfunc, "nb_long", "__long__", fallback="__int__", py3 = "<RESERVED>"),
|
||||
MethodSlot(unaryfunc, "nb_float", "__float__"),
|
||||
MethodSlot(unaryfunc, "nb_oct", "__oct__", ifdef = PyNumberMethods_Py3_GUARD),
|
||||
MethodSlot(unaryfunc, "nb_hex", "__hex__", ifdef = PyNumberMethods_Py3_GUARD),
|
||||
|
||||
# Added in release 2.0
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_add", "__iadd__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_subtract", "__isub__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_multiply", "__imul__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_divide", "__idiv__", ifdef = PyNumberMethods_Py3_GUARD),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_remainder", "__imod__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_power", "__ipow__"), # actually ternaryfunc!!!
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_lshift", "__ilshift__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_rshift", "__irshift__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_and", "__iand__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_xor", "__ixor__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_or", "__ior__"),
|
||||
|
||||
# Added in release 2.2
|
||||
# The following require the Py_TPFLAGS_HAVE_CLASS flag
|
||||
MethodSlot(binaryfunc, "nb_floor_divide", "__floordiv__"),
|
||||
MethodSlot(binaryfunc, "nb_true_divide", "__truediv__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_floor_divide", "__ifloordiv__"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_true_divide", "__itruediv__"),
|
||||
|
||||
# Added in release 2.5
|
||||
MethodSlot(unaryfunc, "nb_index", "__index__"),
|
||||
|
||||
# Added in release 3.5
|
||||
MethodSlot(binaryfunc, "nb_matrix_multiply", "__matmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
|
||||
MethodSlot(ibinaryfunc, "nb_inplace_matrix_multiply", "__imatmul__", ifdef="PY_VERSION_HEX >= 0x03050000"),
|
||||
)
|
||||
|
||||
PySequenceMethods = (
|
||||
MethodSlot(lenfunc, "sq_length", "__len__"),
|
||||
EmptySlot("sq_concat"), # nb_add used instead
|
||||
EmptySlot("sq_repeat"), # nb_multiply used instead
|
||||
SyntheticSlot("sq_item", ["__getitem__"], "0"), #EmptySlot("sq_item"), # mp_subscript used instead
|
||||
MethodSlot(ssizessizeargfunc, "sq_slice", "__getslice__"),
|
||||
EmptySlot("sq_ass_item"), # mp_ass_subscript used instead
|
||||
SyntheticSlot("sq_ass_slice", ["__setslice__", "__delslice__"], "0"),
|
||||
MethodSlot(cmpfunc, "sq_contains", "__contains__"),
|
||||
EmptySlot("sq_inplace_concat"), # nb_inplace_add used instead
|
||||
EmptySlot("sq_inplace_repeat"), # nb_inplace_multiply used instead
|
||||
)
|
||||
|
||||
PyMappingMethods = (
|
||||
MethodSlot(lenfunc, "mp_length", "__len__"),
|
||||
MethodSlot(objargfunc, "mp_subscript", "__getitem__"),
|
||||
SyntheticSlot("mp_ass_subscript", ["__setitem__", "__delitem__"], "0"),
|
||||
)
|
||||
|
||||
PyBufferProcs = (
|
||||
MethodSlot(readbufferproc, "bf_getreadbuffer", "__getreadbuffer__", py3 = False),
|
||||
MethodSlot(writebufferproc, "bf_getwritebuffer", "__getwritebuffer__", py3 = False),
|
||||
MethodSlot(segcountproc, "bf_getsegcount", "__getsegcount__", py3 = False),
|
||||
MethodSlot(charbufferproc, "bf_getcharbuffer", "__getcharbuffer__", py3 = False),
|
||||
|
||||
MethodSlot(getbufferproc, "bf_getbuffer", "__getbuffer__"),
|
||||
MethodSlot(releasebufferproc, "bf_releasebuffer", "__releasebuffer__")
|
||||
)
|
||||
|
||||
PyAsyncMethods = (
|
||||
MethodSlot(unaryfunc, "am_await", "__await__"),
|
||||
MethodSlot(unaryfunc, "am_aiter", "__aiter__"),
|
||||
MethodSlot(unaryfunc, "am_anext", "__anext__"),
|
||||
EmptySlot("am_send", ifdef="PY_VERSION_HEX >= 0x030A00A3"),
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# The main slot table. This table contains descriptors for all the
|
||||
# top-level type slots, beginning with tp_dealloc, in the order they
|
||||
# appear in the type object.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
slot_table = (
|
||||
ConstructorSlot("tp_dealloc", '__dealloc__'),
|
||||
EmptySlot("tp_print", ifdef="PY_VERSION_HEX < 0x030800b4"),
|
||||
EmptySlot("tp_vectorcall_offset", ifdef="PY_VERSION_HEX >= 0x030800b4"),
|
||||
EmptySlot("tp_getattr"),
|
||||
EmptySlot("tp_setattr"),
|
||||
|
||||
# tp_compare (Py2) / tp_reserved (Py3<3.5) / tp_as_async (Py3.5+) is always used as tp_as_async in Py3
|
||||
MethodSlot(cmpfunc, "tp_compare", "__cmp__", ifdef="PY_MAJOR_VERSION < 3"),
|
||||
SuiteSlot(PyAsyncMethods, "__Pyx_PyAsyncMethodsStruct", "tp_as_async", ifdef="PY_MAJOR_VERSION >= 3"),
|
||||
|
||||
MethodSlot(reprfunc, "tp_repr", "__repr__"),
|
||||
|
||||
SuiteSlot(PyNumberMethods, "PyNumberMethods", "tp_as_number"),
|
||||
SuiteSlot(PySequenceMethods, "PySequenceMethods", "tp_as_sequence"),
|
||||
SuiteSlot(PyMappingMethods, "PyMappingMethods", "tp_as_mapping"),
|
||||
|
||||
MethodSlot(hashfunc, "tp_hash", "__hash__", inherited=False), # Py3 checks for __richcmp__
|
||||
MethodSlot(callfunc, "tp_call", "__call__"),
|
||||
MethodSlot(reprfunc, "tp_str", "__str__"),
|
||||
|
||||
SyntheticSlot("tp_getattro", ["__getattr__","__getattribute__"], "0"), #"PyObject_GenericGetAttr"),
|
||||
SyntheticSlot("tp_setattro", ["__setattr__", "__delattr__"], "0"), #"PyObject_GenericSetAttr"),
|
||||
|
||||
SuiteSlot(PyBufferProcs, "PyBufferProcs", "tp_as_buffer"),
|
||||
|
||||
TypeFlagsSlot("tp_flags"),
|
||||
DocStringSlot("tp_doc"),
|
||||
|
||||
GCDependentSlot("tp_traverse"),
|
||||
GCClearReferencesSlot("tp_clear"),
|
||||
|
||||
RichcmpSlot(richcmpfunc, "tp_richcompare", "__richcmp__", inherited=False), # Py3 checks for __hash__
|
||||
|
||||
EmptySlot("tp_weaklistoffset"),
|
||||
|
||||
MethodSlot(getiterfunc, "tp_iter", "__iter__"),
|
||||
MethodSlot(iternextfunc, "tp_iternext", "__next__"),
|
||||
|
||||
MethodTableSlot("tp_methods"),
|
||||
MemberTableSlot("tp_members"),
|
||||
GetSetSlot("tp_getset"),
|
||||
|
||||
BaseClassSlot("tp_base"), #EmptySlot("tp_base"),
|
||||
EmptySlot("tp_dict"),
|
||||
|
||||
SyntheticSlot("tp_descr_get", ["__get__"], "0"),
|
||||
SyntheticSlot("tp_descr_set", ["__set__", "__delete__"], "0"),
|
||||
|
||||
DictOffsetSlot("tp_dictoffset"),
|
||||
|
||||
MethodSlot(initproc, "tp_init", "__init__"),
|
||||
EmptySlot("tp_alloc"), #FixedSlot("tp_alloc", "PyType_GenericAlloc"),
|
||||
InternalMethodSlot("tp_new"),
|
||||
EmptySlot("tp_free"),
|
||||
|
||||
EmptySlot("tp_is_gc"),
|
||||
EmptySlot("tp_bases"),
|
||||
EmptySlot("tp_mro"),
|
||||
EmptySlot("tp_cache"),
|
||||
EmptySlot("tp_subclasses"),
|
||||
EmptySlot("tp_weaklist"),
|
||||
EmptySlot("tp_del"),
|
||||
EmptySlot("tp_version_tag"),
|
||||
EmptySlot("tp_finalize", ifdef="PY_VERSION_HEX >= 0x030400a1"),
|
||||
EmptySlot("tp_vectorcall", ifdef="PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800)"),
|
||||
EmptySlot("tp_print", ifdef="PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000"),
|
||||
# PyPy specific extension - only here to avoid C compiler warnings.
|
||||
EmptySlot("tp_pypy_flags", ifdef="CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000"),
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Descriptors for special methods which don't appear directly
|
||||
# in the type object or its substructures. These methods are
|
||||
# called from slot functions synthesized by Cython.
|
||||
#
|
||||
#------------------------------------------------------------------------------------------
|
||||
|
||||
MethodSlot(initproc, "", "__cinit__")
|
||||
MethodSlot(destructor, "", "__dealloc__")
|
||||
MethodSlot(objobjargproc, "", "__setitem__")
|
||||
MethodSlot(objargproc, "", "__delitem__")
|
||||
MethodSlot(ssizessizeobjargproc, "", "__setslice__")
|
||||
MethodSlot(ssizessizeargproc, "", "__delslice__")
|
||||
MethodSlot(getattrofunc, "", "__getattr__")
|
||||
MethodSlot(getattrofunc, "", "__getattribute__")
|
||||
MethodSlot(setattrofunc, "", "__setattr__")
|
||||
MethodSlot(delattrofunc, "", "__delattr__")
|
||||
MethodSlot(descrgetfunc, "", "__get__")
|
||||
MethodSlot(descrsetfunc, "", "__set__")
|
||||
MethodSlot(descrdelfunc, "", "__delete__")
|
||||
|
||||
|
||||
# Method flags for python-exposed methods.
|
||||
|
||||
method_noargs = "METH_NOARGS"
|
||||
method_onearg = "METH_O"
|
||||
method_varargs = "METH_VARARGS"
|
||||
method_keywords = "METH_KEYWORDS"
|
||||
method_coexist = "METH_COEXIST"
|
||||
@@ -0,0 +1,359 @@
|
||||
#
|
||||
# Nodes used as utilities and support for transforms etc.
|
||||
# These often make up sets including both Nodes and ExprNodes
|
||||
# so it is convenient to have them in a separate module.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import Nodes
|
||||
from . import ExprNodes
|
||||
from .Nodes import Node
|
||||
from .ExprNodes import AtomicExprNode
|
||||
from .PyrexTypes import c_ptr_type
|
||||
|
||||
|
||||
class TempHandle(object):
|
||||
# THIS IS DEPRECATED, USE LetRefNode instead
|
||||
temp = None
|
||||
needs_xdecref = False
|
||||
def __init__(self, type, needs_cleanup=None):
|
||||
self.type = type
|
||||
if needs_cleanup is None:
|
||||
self.needs_cleanup = type.is_pyobject
|
||||
else:
|
||||
self.needs_cleanup = needs_cleanup
|
||||
|
||||
def ref(self, pos):
|
||||
return TempRefNode(pos, handle=self, type=self.type)
|
||||
|
||||
|
||||
class TempRefNode(AtomicExprNode):
|
||||
# THIS IS DEPRECATED, USE LetRefNode instead
|
||||
# handle TempHandle
|
||||
|
||||
def analyse_types(self, env):
|
||||
assert self.type == self.handle.type
|
||||
return self
|
||||
|
||||
def analyse_target_types(self, env):
|
||||
assert self.type == self.handle.type
|
||||
return self
|
||||
|
||||
def analyse_target_declaration(self, env):
|
||||
pass
|
||||
|
||||
def calculate_result_code(self):
|
||||
result = self.handle.temp
|
||||
if result is None: result = "<error>" # might be called and overwritten
|
||||
return result
|
||||
|
||||
def generate_result_code(self, code):
|
||||
pass
|
||||
|
||||
def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
|
||||
if self.type.is_pyobject:
|
||||
rhs.make_owned_reference(code)
|
||||
# TODO: analyse control flow to see if this is necessary
|
||||
code.put_xdecref(self.result(), self.ctype())
|
||||
code.putln('%s = %s;' % (
|
||||
self.result(),
|
||||
rhs.result() if overloaded_assignment else rhs.result_as(self.ctype()),
|
||||
))
|
||||
rhs.generate_post_assignment_code(code)
|
||||
rhs.free_temps(code)
|
||||
|
||||
|
||||
class TempsBlockNode(Node):
|
||||
# THIS IS DEPRECATED, USE LetNode instead
|
||||
|
||||
"""
|
||||
Creates a block which allocates temporary variables.
|
||||
This is used by transforms to output constructs that need
|
||||
to make use of a temporary variable. Simply pass the types
|
||||
of the needed temporaries to the constructor.
|
||||
|
||||
The variables can be referred to using a TempRefNode
|
||||
(which can be constructed by calling get_ref_node).
|
||||
"""
|
||||
|
||||
# temps [TempHandle]
|
||||
# body StatNode
|
||||
|
||||
child_attrs = ["body"]
|
||||
|
||||
def generate_execution_code(self, code):
|
||||
for handle in self.temps:
|
||||
handle.temp = code.funcstate.allocate_temp(
|
||||
handle.type, manage_ref=handle.needs_cleanup)
|
||||
self.body.generate_execution_code(code)
|
||||
for handle in self.temps:
|
||||
if handle.needs_cleanup:
|
||||
if handle.needs_xdecref:
|
||||
code.put_xdecref_clear(handle.temp, handle.type)
|
||||
else:
|
||||
code.put_decref_clear(handle.temp, handle.type)
|
||||
code.funcstate.release_temp(handle.temp)
|
||||
|
||||
def analyse_declarations(self, env):
|
||||
self.body.analyse_declarations(env)
|
||||
|
||||
def analyse_expressions(self, env):
|
||||
self.body = self.body.analyse_expressions(env)
|
||||
return self
|
||||
|
||||
def generate_function_definitions(self, env, code):
|
||||
self.body.generate_function_definitions(env, code)
|
||||
|
||||
def annotate(self, code):
|
||||
self.body.annotate(code)
|
||||
|
||||
|
||||
class ResultRefNode(AtomicExprNode):
|
||||
# A reference to the result of an expression. The result_code
|
||||
# must be set externally (usually a temp name).
|
||||
|
||||
subexprs = []
|
||||
lhs_of_first_assignment = False
|
||||
|
||||
def __init__(self, expression=None, pos=None, type=None, may_hold_none=True, is_temp=False):
|
||||
self.expression = expression
|
||||
self.pos = None
|
||||
self.may_hold_none = may_hold_none
|
||||
if expression is not None:
|
||||
self.pos = expression.pos
|
||||
if hasattr(expression, "type"):
|
||||
self.type = expression.type
|
||||
if pos is not None:
|
||||
self.pos = pos
|
||||
if type is not None:
|
||||
self.type = type
|
||||
if is_temp:
|
||||
self.is_temp = True
|
||||
assert self.pos is not None
|
||||
|
||||
def clone_node(self):
|
||||
# nothing to do here
|
||||
return self
|
||||
|
||||
def type_dependencies(self, env):
|
||||
if self.expression:
|
||||
return self.expression.type_dependencies(env)
|
||||
else:
|
||||
return ()
|
||||
|
||||
def update_expression(self, expression):
|
||||
self.expression = expression
|
||||
if hasattr(expression, "type"):
|
||||
self.type = expression.type
|
||||
|
||||
def analyse_types(self, env):
|
||||
if self.expression is not None:
|
||||
if not self.expression.type:
|
||||
self.expression = self.expression.analyse_types(env)
|
||||
self.type = self.expression.type
|
||||
return self
|
||||
|
||||
def infer_type(self, env):
|
||||
if self.type is not None:
|
||||
return self.type
|
||||
if self.expression is not None:
|
||||
if self.expression.type is not None:
|
||||
return self.expression.type
|
||||
return self.expression.infer_type(env)
|
||||
assert False, "cannot infer type of ResultRefNode"
|
||||
|
||||
def may_be_none(self):
|
||||
if not self.type.is_pyobject:
|
||||
return False
|
||||
return self.may_hold_none
|
||||
|
||||
def _DISABLED_may_be_none(self):
|
||||
# not sure if this is safe - the expression may not be the
|
||||
# only value that gets assigned
|
||||
if self.expression is not None:
|
||||
return self.expression.may_be_none()
|
||||
if self.type is not None:
|
||||
return self.type.is_pyobject
|
||||
return True # play safe
|
||||
|
||||
def is_simple(self):
|
||||
return True
|
||||
|
||||
def result(self):
|
||||
try:
|
||||
return self.result_code
|
||||
except AttributeError:
|
||||
if self.expression is not None:
|
||||
self.result_code = self.expression.result()
|
||||
return self.result_code
|
||||
|
||||
def generate_evaluation_code(self, code):
|
||||
pass
|
||||
|
||||
def generate_result_code(self, code):
|
||||
pass
|
||||
|
||||
def generate_disposal_code(self, code):
|
||||
pass
|
||||
|
||||
def generate_assignment_code(self, rhs, code, overloaded_assignment=False):
|
||||
if self.type.is_pyobject:
|
||||
rhs.make_owned_reference(code)
|
||||
if not self.lhs_of_first_assignment:
|
||||
code.put_decref(self.result(), self.ctype())
|
||||
code.putln('%s = %s;' % (
|
||||
self.result(),
|
||||
rhs.result() if overloaded_assignment else rhs.result_as(self.ctype()),
|
||||
))
|
||||
rhs.generate_post_assignment_code(code)
|
||||
rhs.free_temps(code)
|
||||
|
||||
def allocate_temps(self, env):
|
||||
pass
|
||||
|
||||
def release_temp(self, env):
|
||||
pass
|
||||
|
||||
def free_temps(self, code):
|
||||
pass
|
||||
|
||||
|
||||
class LetNodeMixin:
|
||||
def set_temp_expr(self, lazy_temp):
|
||||
self.lazy_temp = lazy_temp
|
||||
self.temp_expression = lazy_temp.expression
|
||||
|
||||
def setup_temp_expr(self, code):
|
||||
self.temp_expression.generate_evaluation_code(code)
|
||||
self.temp_type = self.temp_expression.type
|
||||
if self.temp_type.is_array:
|
||||
self.temp_type = c_ptr_type(self.temp_type.base_type)
|
||||
self._result_in_temp = self.temp_expression.result_in_temp()
|
||||
if self._result_in_temp:
|
||||
self.temp = self.temp_expression.result()
|
||||
else:
|
||||
self.temp_expression.make_owned_reference(code)
|
||||
self.temp = code.funcstate.allocate_temp(
|
||||
self.temp_type, manage_ref=True)
|
||||
code.putln("%s = %s;" % (self.temp, self.temp_expression.result()))
|
||||
self.temp_expression.generate_disposal_code(code)
|
||||
self.temp_expression.free_temps(code)
|
||||
self.lazy_temp.result_code = self.temp
|
||||
|
||||
def teardown_temp_expr(self, code):
|
||||
if self._result_in_temp:
|
||||
self.temp_expression.generate_disposal_code(code)
|
||||
self.temp_expression.free_temps(code)
|
||||
else:
|
||||
if self.temp_type.is_pyobject:
|
||||
code.put_decref_clear(self.temp, self.temp_type)
|
||||
code.funcstate.release_temp(self.temp)
|
||||
|
||||
|
||||
class EvalWithTempExprNode(ExprNodes.ExprNode, LetNodeMixin):
|
||||
# A wrapper around a subexpression that moves an expression into a
|
||||
# temp variable and provides it to the subexpression.
|
||||
|
||||
subexprs = ['temp_expression', 'subexpression']
|
||||
|
||||
def __init__(self, lazy_temp, subexpression):
|
||||
self.set_temp_expr(lazy_temp)
|
||||
self.pos = subexpression.pos
|
||||
self.subexpression = subexpression
|
||||
# if called after type analysis, we already know the type here
|
||||
self.type = self.subexpression.type
|
||||
|
||||
def infer_type(self, env):
|
||||
return self.subexpression.infer_type(env)
|
||||
|
||||
def may_be_none(self):
|
||||
return self.subexpression.may_be_none()
|
||||
|
||||
def result(self):
|
||||
return self.subexpression.result()
|
||||
|
||||
def analyse_types(self, env):
|
||||
self.temp_expression = self.temp_expression.analyse_types(env)
|
||||
self.lazy_temp.update_expression(self.temp_expression) # overwrite in case it changed
|
||||
self.subexpression = self.subexpression.analyse_types(env)
|
||||
self.type = self.subexpression.type
|
||||
return self
|
||||
|
||||
def free_subexpr_temps(self, code):
|
||||
self.subexpression.free_temps(code)
|
||||
|
||||
def generate_subexpr_disposal_code(self, code):
|
||||
self.subexpression.generate_disposal_code(code)
|
||||
|
||||
def generate_evaluation_code(self, code):
|
||||
self.setup_temp_expr(code)
|
||||
self.subexpression.generate_evaluation_code(code)
|
||||
self.teardown_temp_expr(code)
|
||||
|
||||
|
||||
LetRefNode = ResultRefNode
|
||||
|
||||
|
||||
class LetNode(Nodes.StatNode, LetNodeMixin):
|
||||
# Implements a local temporary variable scope. Imagine this
|
||||
# syntax being present:
|
||||
# let temp = VALUE:
|
||||
# BLOCK (can modify temp)
|
||||
# if temp is an object, decref
|
||||
#
|
||||
# Usually used after analysis phase, but forwards analysis methods
|
||||
# to its children
|
||||
|
||||
child_attrs = ['temp_expression', 'body']
|
||||
|
||||
def __init__(self, lazy_temp, body):
|
||||
self.set_temp_expr(lazy_temp)
|
||||
self.pos = body.pos
|
||||
self.body = body
|
||||
|
||||
def analyse_declarations(self, env):
|
||||
self.temp_expression.analyse_declarations(env)
|
||||
self.body.analyse_declarations(env)
|
||||
|
||||
def analyse_expressions(self, env):
|
||||
self.temp_expression = self.temp_expression.analyse_expressions(env)
|
||||
self.body = self.body.analyse_expressions(env)
|
||||
return self
|
||||
|
||||
def generate_execution_code(self, code):
|
||||
self.setup_temp_expr(code)
|
||||
self.body.generate_execution_code(code)
|
||||
self.teardown_temp_expr(code)
|
||||
|
||||
def generate_function_definitions(self, env, code):
|
||||
self.temp_expression.generate_function_definitions(env, code)
|
||||
self.body.generate_function_definitions(env, code)
|
||||
|
||||
|
||||
class TempResultFromStatNode(ExprNodes.ExprNode):
|
||||
# An ExprNode wrapper around a StatNode that executes the StatNode
|
||||
# body. Requires a ResultRefNode that it sets up to refer to its
|
||||
# own temp result. The StatNode must assign a value to the result
|
||||
# node, which then becomes the result of this node.
|
||||
|
||||
subexprs = []
|
||||
child_attrs = ['body']
|
||||
|
||||
def __init__(self, result_ref, body):
|
||||
self.result_ref = result_ref
|
||||
self.pos = body.pos
|
||||
self.body = body
|
||||
self.type = result_ref.type
|
||||
self.is_temp = 1
|
||||
|
||||
def analyse_declarations(self, env):
|
||||
self.body.analyse_declarations(env)
|
||||
|
||||
def analyse_types(self, env):
|
||||
self.body = self.body.analyse_expressions(env)
|
||||
return self
|
||||
|
||||
def generate_result_code(self, code):
|
||||
self.result_ref.result_code = self.result()
|
||||
self.body.generate_execution_code(code)
|
||||
@@ -0,0 +1,237 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .TreeFragment import parse_from_strings, StringParseContext
|
||||
from . import Symtab
|
||||
from . import Naming
|
||||
from . import Code
|
||||
|
||||
|
||||
class NonManglingModuleScope(Symtab.ModuleScope):
|
||||
|
||||
def __init__(self, prefix, *args, **kw):
|
||||
self.prefix = prefix
|
||||
self.cython_scope = None
|
||||
self.cpp = kw.pop('cpp', False)
|
||||
Symtab.ModuleScope.__init__(self, *args, **kw)
|
||||
|
||||
def add_imported_entry(self, name, entry, pos):
|
||||
entry.used = True
|
||||
return super(NonManglingModuleScope, self).add_imported_entry(name, entry, pos)
|
||||
|
||||
def mangle(self, prefix, name=None):
|
||||
if name:
|
||||
if prefix in (Naming.typeobj_prefix, Naming.func_prefix, Naming.var_prefix, Naming.pyfunc_prefix):
|
||||
# Functions, classes etc. gets a manually defined prefix easily
|
||||
# manually callable instead (the one passed to CythonUtilityCode)
|
||||
prefix = self.prefix
|
||||
return "%s%s" % (prefix, name)
|
||||
else:
|
||||
return Symtab.ModuleScope.mangle(self, prefix)
|
||||
|
||||
|
||||
class CythonUtilityCodeContext(StringParseContext):
|
||||
scope = None
|
||||
|
||||
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=True, absolute_fallback=True):
|
||||
if relative_to:
|
||||
raise AssertionError("Relative imports not supported in utility code.")
|
||||
if module_name != self.module_name:
|
||||
if module_name not in self.modules:
|
||||
raise AssertionError("Only the cython cimport is supported.")
|
||||
else:
|
||||
return self.modules[module_name]
|
||||
|
||||
if self.scope is None:
|
||||
self.scope = NonManglingModuleScope(
|
||||
self.prefix, module_name, parent_module=None, context=self, cpp=self.cpp)
|
||||
|
||||
return self.scope
|
||||
|
||||
|
||||
class CythonUtilityCode(Code.UtilityCodeBase):
|
||||
"""
|
||||
Utility code written in the Cython language itself.
|
||||
|
||||
The @cname decorator can set the cname for a function, method of cdef class.
|
||||
Functions decorated with @cname('c_func_name') get the given cname.
|
||||
|
||||
For cdef classes the rules are as follows:
|
||||
obj struct -> <cname>_obj
|
||||
obj type ptr -> <cname>_type
|
||||
methods -> <class_cname>_<method_cname>
|
||||
|
||||
For methods the cname decorator is optional, but without the decorator the
|
||||
methods will not be prototyped. See Cython.Compiler.CythonScope and
|
||||
tests/run/cythonscope.pyx for examples.
|
||||
"""
|
||||
|
||||
is_cython_utility = True
|
||||
|
||||
def __init__(self, impl, name="__pyxutil", prefix="", requires=None,
|
||||
file=None, from_scope=None, context=None, compiler_directives=None,
|
||||
outer_module_scope=None):
|
||||
# 1) We need to delay the parsing/processing, so that all modules can be
|
||||
# imported without import loops
|
||||
# 2) The same utility code object can be used for multiple source files;
|
||||
# while the generated node trees can be altered in the compilation of a
|
||||
# single file.
|
||||
# Hence, delay any processing until later.
|
||||
context_types = {}
|
||||
if context is not None:
|
||||
from .PyrexTypes import BaseType
|
||||
for key, value in context.items():
|
||||
if isinstance(value, BaseType):
|
||||
context[key] = key
|
||||
context_types[key] = value
|
||||
impl = Code.sub_tempita(impl, context, file, name)
|
||||
self.impl = impl
|
||||
self.name = name
|
||||
self.file = file
|
||||
self.prefix = prefix
|
||||
self.requires = requires or []
|
||||
self.from_scope = from_scope
|
||||
self.outer_module_scope = outer_module_scope
|
||||
self.compiler_directives = compiler_directives
|
||||
self.context_types = context_types
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, CythonUtilityCode):
|
||||
return self._equality_params() == other._equality_params()
|
||||
else:
|
||||
return False
|
||||
|
||||
def _equality_params(self):
|
||||
outer_scope = self.outer_module_scope
|
||||
while isinstance(outer_scope, NonManglingModuleScope):
|
||||
outer_scope = outer_scope.outer_scope
|
||||
return self.impl, outer_scope, self.compiler_directives
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.impl)
|
||||
|
||||
def get_tree(self, entries_only=False, cython_scope=None):
|
||||
from .AnalysedTreeTransforms import AutoTestDictTransform
|
||||
# The AutoTestDictTransform creates the statement "__test__ = {}",
|
||||
# which when copied into the main ModuleNode overwrites
|
||||
# any __test__ in user code; not desired
|
||||
excludes = [AutoTestDictTransform]
|
||||
|
||||
from . import Pipeline, ParseTreeTransforms
|
||||
context = CythonUtilityCodeContext(
|
||||
self.name, compiler_directives=self.compiler_directives,
|
||||
cpp=cython_scope.is_cpp() if cython_scope else False)
|
||||
context.prefix = self.prefix
|
||||
context.cython_scope = cython_scope
|
||||
#context = StringParseContext(self.name)
|
||||
tree = parse_from_strings(
|
||||
self.name, self.impl, context=context, allow_struct_enum_decorator=True)
|
||||
pipeline = Pipeline.create_pipeline(context, 'pyx', exclude_classes=excludes)
|
||||
|
||||
if entries_only:
|
||||
p = []
|
||||
for t in pipeline:
|
||||
p.append(t)
|
||||
if isinstance(p, ParseTreeTransforms.AnalyseDeclarationsTransform):
|
||||
break
|
||||
|
||||
pipeline = p
|
||||
|
||||
transform = ParseTreeTransforms.CnameDirectivesTransform(context)
|
||||
# InterpretCompilerDirectives already does a cdef declarator check
|
||||
#before = ParseTreeTransforms.DecoratorTransform
|
||||
before = ParseTreeTransforms.InterpretCompilerDirectives
|
||||
pipeline = Pipeline.insert_into_pipeline(pipeline, transform,
|
||||
before=before)
|
||||
|
||||
def merge_scope(scope):
|
||||
def merge_scope_transform(module_node):
|
||||
module_node.scope.merge_in(scope)
|
||||
return module_node
|
||||
return merge_scope_transform
|
||||
|
||||
if self.from_scope:
|
||||
pipeline = Pipeline.insert_into_pipeline(
|
||||
pipeline, merge_scope(self.from_scope),
|
||||
before=ParseTreeTransforms.AnalyseDeclarationsTransform)
|
||||
|
||||
for dep in self.requires:
|
||||
if isinstance(dep, CythonUtilityCode) and hasattr(dep, 'tree') and not cython_scope:
|
||||
pipeline = Pipeline.insert_into_pipeline(
|
||||
pipeline, merge_scope(dep.tree.scope),
|
||||
before=ParseTreeTransforms.AnalyseDeclarationsTransform)
|
||||
|
||||
if self.outer_module_scope:
|
||||
# inject outer module between utility code module and builtin module
|
||||
def scope_transform(module_node):
|
||||
module_node.scope.outer_scope = self.outer_module_scope
|
||||
return module_node
|
||||
|
||||
pipeline = Pipeline.insert_into_pipeline(
|
||||
pipeline, scope_transform,
|
||||
before=ParseTreeTransforms.AnalyseDeclarationsTransform)
|
||||
|
||||
if self.context_types:
|
||||
# inject types into module scope
|
||||
def scope_transform(module_node):
|
||||
for name, type in self.context_types.items():
|
||||
entry = module_node.scope.declare_type(name, type, None, visibility='extern')
|
||||
entry.in_cinclude = True
|
||||
return module_node
|
||||
|
||||
pipeline = Pipeline.insert_into_pipeline(
|
||||
pipeline, scope_transform,
|
||||
before=ParseTreeTransforms.AnalyseDeclarationsTransform)
|
||||
|
||||
(err, tree) = Pipeline.run_pipeline(pipeline, tree, printtree=False)
|
||||
assert not err, err
|
||||
self.tree = tree
|
||||
return tree
|
||||
|
||||
def put_code(self, output):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def load_as_string(cls, util_code_name, from_file=None, **kwargs):
|
||||
"""
|
||||
Load a utility code as a string. Returns (proto, implementation)
|
||||
"""
|
||||
util = cls.load(util_code_name, from_file, **kwargs)
|
||||
return util.proto, util.impl # keep line numbers => no lstrip()
|
||||
|
||||
def declare_in_scope(self, dest_scope, used=False, cython_scope=None,
|
||||
whitelist=None):
|
||||
"""
|
||||
Declare all entries from the utility code in dest_scope. Code will only
|
||||
be included for used entries. If module_name is given, declare the
|
||||
type entries with that name.
|
||||
"""
|
||||
tree = self.get_tree(entries_only=True, cython_scope=cython_scope)
|
||||
|
||||
entries = tree.scope.entries
|
||||
entries.pop('__name__')
|
||||
entries.pop('__file__')
|
||||
entries.pop('__builtins__')
|
||||
entries.pop('__doc__')
|
||||
|
||||
for entry in entries.values():
|
||||
entry.utility_code_definition = self
|
||||
entry.used = used
|
||||
|
||||
original_scope = tree.scope
|
||||
dest_scope.merge_in(original_scope, merge_unused=True, whitelist=whitelist)
|
||||
tree.scope = dest_scope
|
||||
|
||||
for dep in self.requires:
|
||||
if dep.is_cython_utility:
|
||||
dep.declare_in_scope(dest_scope, cython_scope=cython_scope)
|
||||
|
||||
return original_scope
|
||||
|
||||
|
||||
def declare_declarations_in_scope(declaration_string, env, private_type=True,
|
||||
*args, **kwargs):
|
||||
"""
|
||||
Declare some declarations given as Cython code in declaration_string
|
||||
in scope env.
|
||||
"""
|
||||
CythonUtilityCode(declaration_string, *args, **kwargs).declare_in_scope(env)
|
||||
@@ -0,0 +1,9 @@
|
||||
# for backwards compatibility
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .. import __version__ as version
|
||||
|
||||
# For 'generated by' header line in C files.
|
||||
|
||||
watermark = str(version)
|
||||
@@ -0,0 +1,55 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
cimport cython
|
||||
|
||||
cdef class TreeVisitor:
|
||||
cdef public list access_path
|
||||
cdef dict dispatch_table
|
||||
|
||||
cpdef visit(self, obj)
|
||||
cdef _visit(self, obj)
|
||||
cdef find_handler(self, obj)
|
||||
cdef _visitchild(self, child, parent, attrname, idx)
|
||||
cdef dict _visitchildren(self, parent, attrs)
|
||||
cpdef visitchildren(self, parent, attrs=*)
|
||||
cdef _raise_compiler_error(self, child, e)
|
||||
|
||||
cdef class VisitorTransform(TreeVisitor):
|
||||
cdef dict _process_children(self, parent, attrs=*)
|
||||
cpdef visitchildren(self, parent, attrs=*, exclude=*)
|
||||
cdef list _flatten_list(self, list orig_list)
|
||||
cdef list _select_attrs(self, attrs, exclude)
|
||||
|
||||
cdef class CythonTransform(VisitorTransform):
|
||||
cdef public context
|
||||
cdef public current_directives
|
||||
|
||||
cdef class ScopeTrackingTransform(CythonTransform):
|
||||
cdef public scope_type
|
||||
cdef public scope_node
|
||||
cdef visit_scope(self, node, scope_type)
|
||||
|
||||
cdef class EnvTransform(CythonTransform):
|
||||
cdef public list env_stack
|
||||
|
||||
cdef class MethodDispatcherTransform(EnvTransform):
|
||||
@cython.final
|
||||
cdef _visit_binop_node(self, node)
|
||||
@cython.final
|
||||
cdef _find_handler(self, match_name, bint has_kwargs)
|
||||
@cython.final
|
||||
cdef _delegate_to_assigned_value(self, node, function, arg_list, kwargs)
|
||||
@cython.final
|
||||
cdef _dispatch_to_handler(self, node, function, arg_list, kwargs)
|
||||
@cython.final
|
||||
cdef _dispatch_to_method_handler(self, attr_name, self_arg,
|
||||
is_unbound_method, type_name,
|
||||
node, function, arg_list, kwargs)
|
||||
|
||||
cdef class RecursiveNodeReplacer(VisitorTransform):
|
||||
cdef public orig_node
|
||||
cdef public new_node
|
||||
|
||||
cdef class NodeFinder(TreeVisitor):
|
||||
cdef node
|
||||
cdef public bint found
|
||||
@@ -0,0 +1,840 @@
|
||||
# cython: infer_types=True
|
||||
# cython: language_level=3
|
||||
# cython: auto_pickle=False
|
||||
|
||||
#
|
||||
# Tree visitor and transform framework
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
from . import TypeSlots
|
||||
from . import Builtin
|
||||
from . import Nodes
|
||||
from . import ExprNodes
|
||||
from . import Errors
|
||||
from . import DebugFlags
|
||||
from . import Future
|
||||
|
||||
import cython
|
||||
|
||||
|
||||
cython.declare(_PRINTABLE=tuple)
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
_PRINTABLE = (bytes, str, int, float)
|
||||
else:
|
||||
_PRINTABLE = (str, unicode, long, int, float)
|
||||
|
||||
|
||||
class TreeVisitor(object):
|
||||
"""
|
||||
Base class for writing visitors for a Cython tree, contains utilities for
|
||||
recursing such trees using visitors. Each node is
|
||||
expected to have a child_attrs iterable containing the names of attributes
|
||||
containing child nodes or lists of child nodes. Lists are not considered
|
||||
part of the tree structure (i.e. contained nodes are considered direct
|
||||
children of the parent node).
|
||||
|
||||
visit_children visits each of the children of a given node (see the visit_children
|
||||
documentation). When recursing the tree using visit_children, an attribute
|
||||
access_path is maintained which gives information about the current location
|
||||
in the tree as a stack of tuples: (parent_node, attrname, index), representing
|
||||
the node, attribute and optional list index that was taken in each step in the path to
|
||||
the current node.
|
||||
|
||||
Example:
|
||||
|
||||
>>> class SampleNode(object):
|
||||
... child_attrs = ["head", "body"]
|
||||
... def __init__(self, value, head=None, body=None):
|
||||
... self.value = value
|
||||
... self.head = head
|
||||
... self.body = body
|
||||
... def __repr__(self): return "SampleNode(%s)" % self.value
|
||||
...
|
||||
>>> tree = SampleNode(0, SampleNode(1), [SampleNode(2), SampleNode(3)])
|
||||
>>> class MyVisitor(TreeVisitor):
|
||||
... def visit_SampleNode(self, node):
|
||||
... print("in %s %s" % (node.value, self.access_path))
|
||||
... self.visitchildren(node)
|
||||
... print("out %s" % node.value)
|
||||
...
|
||||
>>> MyVisitor().visit(tree)
|
||||
in 0 []
|
||||
in 1 [(SampleNode(0), 'head', None)]
|
||||
out 1
|
||||
in 2 [(SampleNode(0), 'body', 0)]
|
||||
out 2
|
||||
in 3 [(SampleNode(0), 'body', 1)]
|
||||
out 3
|
||||
out 0
|
||||
"""
|
||||
def __init__(self):
|
||||
super(TreeVisitor, self).__init__()
|
||||
self.dispatch_table = {}
|
||||
self.access_path = []
|
||||
|
||||
def dump_node(self, node):
|
||||
ignored = list(node.child_attrs or []) + [
|
||||
u'child_attrs', u'pos', u'gil_message', u'cpp_message', u'subexprs']
|
||||
values = []
|
||||
pos = getattr(node, 'pos', None)
|
||||
if pos:
|
||||
source = pos[0]
|
||||
if source:
|
||||
import os.path
|
||||
source = os.path.basename(source.get_description())
|
||||
values.append(u'%s:%s:%s' % (source, pos[1], pos[2]))
|
||||
attribute_names = dir(node)
|
||||
for attr in attribute_names:
|
||||
if attr in ignored:
|
||||
continue
|
||||
if attr.startswith('_') or attr.endswith('_'):
|
||||
continue
|
||||
try:
|
||||
value = getattr(node, attr)
|
||||
except AttributeError:
|
||||
continue
|
||||
if value is None or value == 0:
|
||||
continue
|
||||
elif isinstance(value, list):
|
||||
value = u'[...]/%d' % len(value)
|
||||
elif not isinstance(value, _PRINTABLE):
|
||||
continue
|
||||
else:
|
||||
value = repr(value)
|
||||
values.append(u'%s = %s' % (attr, value))
|
||||
return u'%s(%s)' % (node.__class__.__name__, u',\n '.join(values))
|
||||
|
||||
def _find_node_path(self, stacktrace):
|
||||
import os.path
|
||||
last_traceback = stacktrace
|
||||
nodes = []
|
||||
while hasattr(stacktrace, 'tb_frame'):
|
||||
frame = stacktrace.tb_frame
|
||||
node = frame.f_locals.get(u'self')
|
||||
if isinstance(node, Nodes.Node):
|
||||
code = frame.f_code
|
||||
method_name = code.co_name
|
||||
pos = (os.path.basename(code.co_filename),
|
||||
frame.f_lineno)
|
||||
nodes.append((node, method_name, pos))
|
||||
last_traceback = stacktrace
|
||||
stacktrace = stacktrace.tb_next
|
||||
return (last_traceback, nodes)
|
||||
|
||||
def _raise_compiler_error(self, child, e):
|
||||
trace = ['']
|
||||
for parent, attribute, index in self.access_path:
|
||||
node = getattr(parent, attribute)
|
||||
if index is None:
|
||||
index = ''
|
||||
else:
|
||||
node = node[index]
|
||||
index = u'[%d]' % index
|
||||
trace.append(u'%s.%s%s = %s' % (
|
||||
parent.__class__.__name__, attribute, index,
|
||||
self.dump_node(node)))
|
||||
stacktrace, called_nodes = self._find_node_path(sys.exc_info()[2])
|
||||
last_node = child
|
||||
for node, method_name, pos in called_nodes:
|
||||
last_node = node
|
||||
trace.append(u"File '%s', line %d, in %s: %s" % (
|
||||
pos[0], pos[1], method_name, self.dump_node(node)))
|
||||
raise Errors.CompilerCrash(
|
||||
getattr(last_node, 'pos', None), self.__class__.__name__,
|
||||
u'\n'.join(trace), e, stacktrace)
|
||||
|
||||
@cython.final
|
||||
def find_handler(self, obj):
|
||||
# to resolve, try entire hierarchy
|
||||
cls = type(obj)
|
||||
pattern = "visit_%s"
|
||||
mro = inspect.getmro(cls)
|
||||
for mro_cls in mro:
|
||||
handler_method = getattr(self, pattern % mro_cls.__name__, None)
|
||||
if handler_method is not None:
|
||||
return handler_method
|
||||
print(type(self), cls)
|
||||
if self.access_path:
|
||||
print(self.access_path)
|
||||
print(self.access_path[-1][0].pos)
|
||||
print(self.access_path[-1][0].__dict__)
|
||||
raise RuntimeError("Visitor %r does not accept object: %s" % (self, obj))
|
||||
|
||||
def visit(self, obj):
|
||||
return self._visit(obj)
|
||||
|
||||
@cython.final
|
||||
def _visit(self, obj):
|
||||
try:
|
||||
try:
|
||||
handler_method = self.dispatch_table[type(obj)]
|
||||
except KeyError:
|
||||
handler_method = self.find_handler(obj)
|
||||
self.dispatch_table[type(obj)] = handler_method
|
||||
return handler_method(obj)
|
||||
except Errors.CompileError:
|
||||
raise
|
||||
except Errors.AbortError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if DebugFlags.debug_no_exception_intercept:
|
||||
raise
|
||||
self._raise_compiler_error(obj, e)
|
||||
|
||||
@cython.final
|
||||
def _visitchild(self, child, parent, attrname, idx):
|
||||
self.access_path.append((parent, attrname, idx))
|
||||
result = self._visit(child)
|
||||
self.access_path.pop()
|
||||
return result
|
||||
|
||||
def visitchildren(self, parent, attrs=None):
|
||||
return self._visitchildren(parent, attrs)
|
||||
|
||||
@cython.final
|
||||
@cython.locals(idx=cython.Py_ssize_t)
|
||||
def _visitchildren(self, parent, attrs):
|
||||
"""
|
||||
Visits the children of the given parent. If parent is None, returns
|
||||
immediately (returning None).
|
||||
|
||||
The return value is a dictionary giving the results for each
|
||||
child (mapping the attribute name to either the return value
|
||||
or a list of return values (in the case of multiple children
|
||||
in an attribute)).
|
||||
"""
|
||||
if parent is None: return None
|
||||
result = {}
|
||||
for attr in parent.child_attrs:
|
||||
if attrs is not None and attr not in attrs: continue
|
||||
child = getattr(parent, attr)
|
||||
if child is not None:
|
||||
if type(child) is list:
|
||||
childretval = [self._visitchild(x, parent, attr, idx) for idx, x in enumerate(child)]
|
||||
else:
|
||||
childretval = self._visitchild(child, parent, attr, None)
|
||||
assert not isinstance(childretval, list), 'Cannot insert list here: %s in %r' % (attr, parent)
|
||||
result[attr] = childretval
|
||||
return result
|
||||
|
||||
|
||||
class VisitorTransform(TreeVisitor):
|
||||
"""
|
||||
A tree transform is a base class for visitors that wants to do stream
|
||||
processing of the structure (rather than attributes etc.) of a tree.
|
||||
|
||||
It implements __call__ to simply visit the argument node.
|
||||
|
||||
It requires the visitor methods to return the nodes which should take
|
||||
the place of the visited node in the result tree (which can be the same
|
||||
or one or more replacement). Specifically, if the return value from
|
||||
a visitor method is:
|
||||
|
||||
- [] or None; the visited node will be removed (set to None if an attribute and
|
||||
removed if in a list)
|
||||
- A single node; the visited node will be replaced by the returned node.
|
||||
- A list of nodes; the visited nodes will be replaced by all the nodes in the
|
||||
list. This will only work if the node was already a member of a list; if it
|
||||
was not, an exception will be raised. (Typically you want to ensure that you
|
||||
are within a StatListNode or similar before doing this.)
|
||||
"""
|
||||
def visitchildren(self, parent, attrs=None, exclude=None):
|
||||
# generic def entry point for calls from Python subclasses
|
||||
if exclude is not None:
|
||||
attrs = self._select_attrs(parent.child_attrs if attrs is None else attrs, exclude)
|
||||
return self._process_children(parent, attrs)
|
||||
|
||||
@cython.final
|
||||
def _select_attrs(self, attrs, exclude):
|
||||
return [name for name in attrs if name not in exclude]
|
||||
|
||||
@cython.final
|
||||
def _process_children(self, parent, attrs=None):
|
||||
# fast cdef entry point for calls from Cython subclasses
|
||||
result = self._visitchildren(parent, attrs)
|
||||
for attr, newnode in result.items():
|
||||
if type(newnode) is list:
|
||||
newnode = self._flatten_list(newnode)
|
||||
setattr(parent, attr, newnode)
|
||||
return result
|
||||
|
||||
@cython.final
|
||||
def _flatten_list(self, orig_list):
|
||||
# Flatten the list one level and remove any None
|
||||
newlist = []
|
||||
for x in orig_list:
|
||||
if x is not None:
|
||||
if type(x) is list:
|
||||
newlist.extend(x)
|
||||
else:
|
||||
newlist.append(x)
|
||||
return newlist
|
||||
|
||||
def recurse_to_children(self, node):
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
def __call__(self, root):
|
||||
return self._visit(root)
|
||||
|
||||
|
||||
class CythonTransform(VisitorTransform):
|
||||
"""
|
||||
Certain common conventions and utilities for Cython transforms.
|
||||
|
||||
- Sets up the context of the pipeline in self.context
|
||||
- Tracks directives in effect in self.current_directives
|
||||
"""
|
||||
def __init__(self, context):
|
||||
super(CythonTransform, self).__init__()
|
||||
self.context = context
|
||||
|
||||
def __call__(self, node):
|
||||
from . import ModuleNode
|
||||
if isinstance(node, ModuleNode.ModuleNode):
|
||||
self.current_directives = node.directives
|
||||
return super(CythonTransform, self).__call__(node)
|
||||
|
||||
def visit_CompilerDirectivesNode(self, node):
|
||||
old = self.current_directives
|
||||
self.current_directives = node.directives
|
||||
self._process_children(node)
|
||||
self.current_directives = old
|
||||
return node
|
||||
|
||||
def visit_Node(self, node):
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
|
||||
class ScopeTrackingTransform(CythonTransform):
|
||||
# Keeps track of type of scopes
|
||||
#scope_type: can be either of 'module', 'function', 'cclass', 'pyclass', 'struct'
|
||||
#scope_node: the node that owns the current scope
|
||||
|
||||
def visit_ModuleNode(self, node):
|
||||
self.scope_type = 'module'
|
||||
self.scope_node = node
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
def visit_scope(self, node, scope_type):
|
||||
prev = self.scope_type, self.scope_node
|
||||
self.scope_type = scope_type
|
||||
self.scope_node = node
|
||||
self._process_children(node)
|
||||
self.scope_type, self.scope_node = prev
|
||||
return node
|
||||
|
||||
def visit_CClassDefNode(self, node):
|
||||
return self.visit_scope(node, 'cclass')
|
||||
|
||||
def visit_PyClassDefNode(self, node):
|
||||
return self.visit_scope(node, 'pyclass')
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
return self.visit_scope(node, 'function')
|
||||
|
||||
def visit_CStructOrUnionDefNode(self, node):
|
||||
return self.visit_scope(node, 'struct')
|
||||
|
||||
|
||||
class EnvTransform(CythonTransform):
|
||||
"""
|
||||
This transformation keeps a stack of the environments.
|
||||
"""
|
||||
def __call__(self, root):
|
||||
self.env_stack = []
|
||||
self.enter_scope(root, root.scope)
|
||||
return super(EnvTransform, self).__call__(root)
|
||||
|
||||
def current_env(self):
|
||||
return self.env_stack[-1][1]
|
||||
|
||||
def current_scope_node(self):
|
||||
return self.env_stack[-1][0]
|
||||
|
||||
def global_scope(self):
|
||||
return self.current_env().global_scope()
|
||||
|
||||
def enter_scope(self, node, scope):
|
||||
self.env_stack.append((node, scope))
|
||||
|
||||
def exit_scope(self):
|
||||
self.env_stack.pop()
|
||||
|
||||
def visit_FuncDefNode(self, node):
|
||||
self.enter_scope(node, node.local_scope)
|
||||
self._process_children(node)
|
||||
self.exit_scope()
|
||||
return node
|
||||
|
||||
def visit_GeneratorBodyDefNode(self, node):
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
def visit_ClassDefNode(self, node):
|
||||
self.enter_scope(node, node.scope)
|
||||
self._process_children(node)
|
||||
self.exit_scope()
|
||||
return node
|
||||
|
||||
def visit_CStructOrUnionDefNode(self, node):
|
||||
self.enter_scope(node, node.scope)
|
||||
self._process_children(node)
|
||||
self.exit_scope()
|
||||
return node
|
||||
|
||||
def visit_ScopedExprNode(self, node):
|
||||
if node.expr_scope:
|
||||
self.enter_scope(node, node.expr_scope)
|
||||
self._process_children(node)
|
||||
self.exit_scope()
|
||||
else:
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
def visit_CArgDeclNode(self, node):
|
||||
# default arguments are evaluated in the outer scope
|
||||
if node.default:
|
||||
attrs = [attr for attr in node.child_attrs if attr != 'default']
|
||||
self._process_children(node, attrs)
|
||||
self.enter_scope(node, self.current_env().outer_scope)
|
||||
self.visitchildren(node, ('default',))
|
||||
self.exit_scope()
|
||||
else:
|
||||
self._process_children(node)
|
||||
return node
|
||||
|
||||
|
||||
class NodeRefCleanupMixin(object):
|
||||
"""
|
||||
Clean up references to nodes that were replaced.
|
||||
|
||||
NOTE: this implementation assumes that the replacement is
|
||||
done first, before hitting any further references during
|
||||
normal tree traversal. This needs to be arranged by calling
|
||||
"self.visitchildren()" at a proper place in the transform
|
||||
and by ordering the "child_attrs" of nodes appropriately.
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
super(NodeRefCleanupMixin, self).__init__(*args)
|
||||
self._replacements = {}
|
||||
|
||||
def visit_CloneNode(self, node):
|
||||
arg = node.arg
|
||||
if arg not in self._replacements:
|
||||
self.visitchildren(arg)
|
||||
node.arg = self._replacements.get(arg, arg)
|
||||
return node
|
||||
|
||||
def visit_ResultRefNode(self, node):
|
||||
expr = node.expression
|
||||
if expr is None or expr not in self._replacements:
|
||||
self.visitchildren(node)
|
||||
expr = node.expression
|
||||
if expr is not None:
|
||||
node.expression = self._replacements.get(expr, expr)
|
||||
return node
|
||||
|
||||
def replace(self, node, replacement):
|
||||
self._replacements[node] = replacement
|
||||
return replacement
|
||||
|
||||
|
||||
find_special_method_for_binary_operator = {
|
||||
'<': '__lt__',
|
||||
'<=': '__le__',
|
||||
'==': '__eq__',
|
||||
'!=': '__ne__',
|
||||
'>=': '__ge__',
|
||||
'>': '__gt__',
|
||||
'+': '__add__',
|
||||
'&': '__and__',
|
||||
'/': '__div__',
|
||||
'//': '__floordiv__',
|
||||
'<<': '__lshift__',
|
||||
'%': '__mod__',
|
||||
'*': '__mul__',
|
||||
'|': '__or__',
|
||||
'**': '__pow__',
|
||||
'>>': '__rshift__',
|
||||
'-': '__sub__',
|
||||
'^': '__xor__',
|
||||
'in': '__contains__',
|
||||
}.get
|
||||
|
||||
|
||||
find_special_method_for_unary_operator = {
|
||||
'not': '__not__',
|
||||
'~': '__inv__',
|
||||
'-': '__neg__',
|
||||
'+': '__pos__',
|
||||
}.get
|
||||
|
||||
|
||||
class MethodDispatcherTransform(EnvTransform):
|
||||
"""
|
||||
Base class for transformations that want to intercept on specific
|
||||
builtin functions or methods of builtin types, including special
|
||||
methods triggered by Python operators. Must run after declaration
|
||||
analysis when entries were assigned.
|
||||
|
||||
Naming pattern for handler methods is as follows:
|
||||
|
||||
* builtin functions: _handle_(general|simple|any)_function_NAME
|
||||
|
||||
* builtin methods: _handle_(general|simple|any)_method_TYPENAME_METHODNAME
|
||||
"""
|
||||
# only visit call nodes and Python operations
|
||||
def visit_GeneralCallNode(self, node):
|
||||
self._process_children(node)
|
||||
function = node.function
|
||||
if not function.type.is_pyobject:
|
||||
return node
|
||||
arg_tuple = node.positional_args
|
||||
if not isinstance(arg_tuple, ExprNodes.TupleNode):
|
||||
return node
|
||||
keyword_args = node.keyword_args
|
||||
if keyword_args and not isinstance(keyword_args, ExprNodes.DictNode):
|
||||
# can't handle **kwargs
|
||||
return node
|
||||
args = arg_tuple.args
|
||||
return self._dispatch_to_handler(node, function, args, keyword_args)
|
||||
|
||||
def visit_SimpleCallNode(self, node):
|
||||
self._process_children(node)
|
||||
function = node.function
|
||||
if function.type.is_pyobject:
|
||||
arg_tuple = node.arg_tuple
|
||||
if not isinstance(arg_tuple, ExprNodes.TupleNode):
|
||||
return node
|
||||
args = arg_tuple.args
|
||||
else:
|
||||
args = node.args
|
||||
return self._dispatch_to_handler(node, function, args, None)
|
||||
|
||||
def visit_PrimaryCmpNode(self, node):
|
||||
if node.cascade:
|
||||
# not currently handled below
|
||||
self._process_children(node)
|
||||
return node
|
||||
return self._visit_binop_node(node)
|
||||
|
||||
def visit_BinopNode(self, node):
|
||||
return self._visit_binop_node(node)
|
||||
|
||||
def _visit_binop_node(self, node):
|
||||
self._process_children(node)
|
||||
# FIXME: could special case 'not_in'
|
||||
special_method_name = find_special_method_for_binary_operator(node.operator)
|
||||
if special_method_name:
|
||||
operand1, operand2 = node.operand1, node.operand2
|
||||
if special_method_name == '__contains__':
|
||||
operand1, operand2 = operand2, operand1
|
||||
elif special_method_name == '__div__':
|
||||
if Future.division in self.current_env().global_scope().context.future_directives:
|
||||
special_method_name = '__truediv__'
|
||||
obj_type = operand1.type
|
||||
if obj_type.is_builtin_type:
|
||||
type_name = obj_type.name
|
||||
else:
|
||||
type_name = "object" # safety measure
|
||||
node = self._dispatch_to_method_handler(
|
||||
special_method_name, None, False, type_name,
|
||||
node, None, [operand1, operand2], None)
|
||||
return node
|
||||
|
||||
def visit_UnopNode(self, node):
|
||||
self._process_children(node)
|
||||
special_method_name = find_special_method_for_unary_operator(node.operator)
|
||||
if special_method_name:
|
||||
operand = node.operand
|
||||
obj_type = operand.type
|
||||
if obj_type.is_builtin_type:
|
||||
type_name = obj_type.name
|
||||
else:
|
||||
type_name = "object" # safety measure
|
||||
node = self._dispatch_to_method_handler(
|
||||
special_method_name, None, False, type_name,
|
||||
node, None, [operand], None)
|
||||
return node
|
||||
|
||||
### dispatch to specific handlers
|
||||
|
||||
def _find_handler(self, match_name, has_kwargs):
|
||||
call_type = has_kwargs and 'general' or 'simple'
|
||||
handler = getattr(self, '_handle_%s_%s' % (call_type, match_name), None)
|
||||
if handler is None:
|
||||
handler = getattr(self, '_handle_any_%s' % match_name, None)
|
||||
return handler
|
||||
|
||||
def _delegate_to_assigned_value(self, node, function, arg_list, kwargs):
|
||||
assignment = function.cf_state[0]
|
||||
value = assignment.rhs
|
||||
if value.is_name:
|
||||
if not value.entry or len(value.entry.cf_assignments) > 1:
|
||||
# the variable might have been reassigned => play safe
|
||||
return node
|
||||
elif value.is_attribute and value.obj.is_name:
|
||||
if not value.obj.entry or len(value.obj.entry.cf_assignments) > 1:
|
||||
# the underlying variable might have been reassigned => play safe
|
||||
return node
|
||||
else:
|
||||
return node
|
||||
return self._dispatch_to_handler(
|
||||
node, value, arg_list, kwargs)
|
||||
|
||||
def _dispatch_to_handler(self, node, function, arg_list, kwargs):
|
||||
if function.is_name:
|
||||
# we only consider functions that are either builtin
|
||||
# Python functions or builtins that were already replaced
|
||||
# into a C function call (defined in the builtin scope)
|
||||
if not function.entry:
|
||||
return node
|
||||
entry = function.entry
|
||||
is_builtin = (
|
||||
entry.is_builtin or
|
||||
entry is self.current_env().builtin_scope().lookup_here(function.name))
|
||||
if not is_builtin:
|
||||
if function.cf_state and function.cf_state.is_single:
|
||||
# we know the value of the variable
|
||||
# => see if it's usable instead
|
||||
return self._delegate_to_assigned_value(
|
||||
node, function, arg_list, kwargs)
|
||||
if arg_list and entry.is_cmethod and entry.scope and entry.scope.parent_type.is_builtin_type:
|
||||
if entry.scope.parent_type is arg_list[0].type:
|
||||
# Optimised (unbound) method of a builtin type => try to "de-optimise".
|
||||
return self._dispatch_to_method_handler(
|
||||
entry.name, self_arg=None, is_unbound_method=True,
|
||||
type_name=entry.scope.parent_type.name,
|
||||
node=node, function=function, arg_list=arg_list, kwargs=kwargs)
|
||||
return node
|
||||
function_handler = self._find_handler(
|
||||
"function_%s" % function.name, kwargs)
|
||||
if function_handler is None:
|
||||
return self._handle_function(node, function.name, function, arg_list, kwargs)
|
||||
if kwargs:
|
||||
return function_handler(node, function, arg_list, kwargs)
|
||||
else:
|
||||
return function_handler(node, function, arg_list)
|
||||
elif function.is_attribute:
|
||||
attr_name = function.attribute
|
||||
if function.type.is_pyobject:
|
||||
self_arg = function.obj
|
||||
elif node.self and function.entry:
|
||||
entry = function.entry.as_variable
|
||||
if not entry or not entry.is_builtin:
|
||||
return node
|
||||
# C implementation of a Python builtin method - see if we find further matches
|
||||
self_arg = node.self
|
||||
arg_list = arg_list[1:] # drop CloneNode of self argument
|
||||
else:
|
||||
return node
|
||||
obj_type = self_arg.type
|
||||
is_unbound_method = False
|
||||
if obj_type.is_builtin_type:
|
||||
if obj_type is Builtin.type_type and self_arg.is_name and arg_list and arg_list[0].type.is_pyobject:
|
||||
# calling an unbound method like 'list.append(L,x)'
|
||||
# (ignoring 'type.mro()' here ...)
|
||||
type_name = self_arg.name
|
||||
self_arg = None
|
||||
is_unbound_method = True
|
||||
else:
|
||||
type_name = obj_type.name
|
||||
else:
|
||||
type_name = "object" # safety measure
|
||||
return self._dispatch_to_method_handler(
|
||||
attr_name, self_arg, is_unbound_method, type_name,
|
||||
node, function, arg_list, kwargs)
|
||||
else:
|
||||
return node
|
||||
|
||||
def _dispatch_to_method_handler(self, attr_name, self_arg,
|
||||
is_unbound_method, type_name,
|
||||
node, function, arg_list, kwargs):
|
||||
method_handler = self._find_handler(
|
||||
"method_%s_%s" % (type_name, attr_name), kwargs)
|
||||
if method_handler is None:
|
||||
if (attr_name in TypeSlots.method_name_to_slot
|
||||
or attr_name == '__new__'):
|
||||
method_handler = self._find_handler(
|
||||
"slot%s" % attr_name, kwargs)
|
||||
if method_handler is None:
|
||||
return self._handle_method(
|
||||
node, type_name, attr_name, function,
|
||||
arg_list, is_unbound_method, kwargs)
|
||||
if self_arg is not None:
|
||||
arg_list = [self_arg] + list(arg_list)
|
||||
if kwargs:
|
||||
result = method_handler(
|
||||
node, function, arg_list, is_unbound_method, kwargs)
|
||||
else:
|
||||
result = method_handler(
|
||||
node, function, arg_list, is_unbound_method)
|
||||
return result
|
||||
|
||||
def _handle_function(self, node, function_name, function, arg_list, kwargs):
|
||||
"""Fallback handler"""
|
||||
return node
|
||||
|
||||
def _handle_method(self, node, type_name, attr_name, function,
|
||||
arg_list, is_unbound_method, kwargs):
|
||||
"""Fallback handler"""
|
||||
return node
|
||||
|
||||
|
||||
class RecursiveNodeReplacer(VisitorTransform):
|
||||
"""
|
||||
Recursively replace all occurrences of a node in a subtree by
|
||||
another node.
|
||||
"""
|
||||
def __init__(self, orig_node, new_node):
|
||||
super(RecursiveNodeReplacer, self).__init__()
|
||||
self.orig_node, self.new_node = orig_node, new_node
|
||||
|
||||
def visit_CloneNode(self, node):
|
||||
if node is self.orig_node:
|
||||
return self.new_node
|
||||
if node.arg is self.orig_node:
|
||||
node.arg = self.new_node
|
||||
return node
|
||||
|
||||
def visit_Node(self, node):
|
||||
self._process_children(node)
|
||||
if node is self.orig_node:
|
||||
return self.new_node
|
||||
else:
|
||||
return node
|
||||
|
||||
def recursively_replace_node(tree, old_node, new_node):
|
||||
replace_in = RecursiveNodeReplacer(old_node, new_node)
|
||||
replace_in(tree)
|
||||
|
||||
|
||||
class NodeFinder(TreeVisitor):
|
||||
"""
|
||||
Find out if a node appears in a subtree.
|
||||
"""
|
||||
def __init__(self, node):
|
||||
super(NodeFinder, self).__init__()
|
||||
self.node = node
|
||||
self.found = False
|
||||
|
||||
def visit_Node(self, node):
|
||||
if self.found:
|
||||
pass # short-circuit
|
||||
elif node is self.node:
|
||||
self.found = True
|
||||
else:
|
||||
self._visitchildren(node, None)
|
||||
|
||||
def tree_contains(tree, node):
|
||||
finder = NodeFinder(node)
|
||||
finder.visit(tree)
|
||||
return finder.found
|
||||
|
||||
|
||||
# Utils
|
||||
def replace_node(ptr, value):
|
||||
"""Replaces a node. ptr is of the form used on the access path stack
|
||||
(parent, attrname, listidx|None)
|
||||
"""
|
||||
parent, attrname, listidx = ptr
|
||||
if listidx is None:
|
||||
setattr(parent, attrname, value)
|
||||
else:
|
||||
getattr(parent, attrname)[listidx] = value
|
||||
|
||||
|
||||
class PrintTree(TreeVisitor):
|
||||
"""Prints a representation of the tree to standard output.
|
||||
Subclass and override repr_of to provide more information
|
||||
about nodes. """
|
||||
def __init__(self, start=None, end=None):
|
||||
TreeVisitor.__init__(self)
|
||||
self._indent = ""
|
||||
if start is not None or end is not None:
|
||||
self._line_range = (start or 0, end or 2**30)
|
||||
else:
|
||||
self._line_range = None
|
||||
|
||||
def indent(self):
|
||||
self._indent += " "
|
||||
|
||||
def unindent(self):
|
||||
self._indent = self._indent[:-2]
|
||||
|
||||
def __call__(self, tree, phase=None):
|
||||
print("Parse tree dump at phase '%s'" % phase)
|
||||
self.visit(tree)
|
||||
return tree
|
||||
|
||||
# Don't do anything about process_list, the defaults gives
|
||||
# nice-looking name[idx] nodes which will visually appear
|
||||
# under the parent-node, not displaying the list itself in
|
||||
# the hierarchy.
|
||||
def visit_Node(self, node):
|
||||
self._print_node(node)
|
||||
self.indent()
|
||||
self.visitchildren(node)
|
||||
self.unindent()
|
||||
return node
|
||||
|
||||
def visit_CloneNode(self, node):
|
||||
self._print_node(node)
|
||||
self.indent()
|
||||
line = node.pos[1]
|
||||
if self._line_range is None or self._line_range[0] <= line <= self._line_range[1]:
|
||||
print("%s- %s: %s" % (self._indent, 'arg', self.repr_of(node.arg)))
|
||||
self.indent()
|
||||
self.visitchildren(node.arg)
|
||||
self.unindent()
|
||||
self.unindent()
|
||||
return node
|
||||
|
||||
def _print_node(self, node):
|
||||
line = node.pos[1]
|
||||
if self._line_range is None or self._line_range[0] <= line <= self._line_range[1]:
|
||||
if len(self.access_path) == 0:
|
||||
name = "(root)"
|
||||
else:
|
||||
parent, attr, idx = self.access_path[-1]
|
||||
if idx is not None:
|
||||
name = "%s[%d]" % (attr, idx)
|
||||
else:
|
||||
name = attr
|
||||
print("%s- %s: %s" % (self._indent, name, self.repr_of(node)))
|
||||
|
||||
def repr_of(self, node):
|
||||
if node is None:
|
||||
return "(none)"
|
||||
else:
|
||||
result = node.__class__.__name__
|
||||
if isinstance(node, ExprNodes.NameNode):
|
||||
result += "(type=%s, name=\"%s\")" % (repr(node.type), node.name)
|
||||
elif isinstance(node, Nodes.DefNode):
|
||||
result += "(name=\"%s\")" % node.name
|
||||
elif isinstance(node, ExprNodes.ExprNode):
|
||||
t = node.type
|
||||
result += "(type=%s)" % repr(t)
|
||||
elif node.pos:
|
||||
pos = node.pos
|
||||
path = pos[0].get_description()
|
||||
if '/' in path:
|
||||
path = path.split('/')[-1]
|
||||
if '\\' in path:
|
||||
path = path.split('\\')[-1]
|
||||
result += "(pos=(%s:%s:%s))" % (path, pos[1], pos[2])
|
||||
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
@@ -0,0 +1 @@
|
||||
# empty file
|
||||
@@ -0,0 +1,348 @@
|
||||
"""
|
||||
A Cython plugin for coverage.py
|
||||
|
||||
Requires the coverage package at least in version 4.0 (which added the plugin API).
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import os.path
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
from coverage.plugin import CoveragePlugin, FileTracer, FileReporter # requires coverage.py 4.0+
|
||||
from coverage.files import canonical_filename
|
||||
|
||||
from .Utils import find_root_package_dir, is_package_dir, open_source_file
|
||||
|
||||
|
||||
from . import __version__
|
||||
|
||||
|
||||
C_FILE_EXTENSIONS = ['.c', '.cpp', '.cc', '.cxx']
|
||||
MODULE_FILE_EXTENSIONS = set(['.py', '.pyx', '.pxd'] + C_FILE_EXTENSIONS)
|
||||
|
||||
|
||||
def _find_c_source(base_path):
|
||||
file_exists = os.path.exists
|
||||
for ext in C_FILE_EXTENSIONS:
|
||||
file_name = base_path + ext
|
||||
if file_exists(file_name):
|
||||
return file_name
|
||||
return None
|
||||
|
||||
|
||||
def _find_dep_file_path(main_file, file_path, relative_path_search=False):
|
||||
abs_path = os.path.abspath(file_path)
|
||||
if not os.path.exists(abs_path) and (file_path.endswith('.pxi') or
|
||||
relative_path_search):
|
||||
# files are looked up relative to the main source file
|
||||
rel_file_path = os.path.join(os.path.dirname(main_file), file_path)
|
||||
if os.path.exists(rel_file_path):
|
||||
abs_path = os.path.abspath(rel_file_path)
|
||||
# search sys.path for external locations if a valid file hasn't been found
|
||||
if not os.path.exists(abs_path):
|
||||
for sys_path in sys.path:
|
||||
test_path = os.path.realpath(os.path.join(sys_path, file_path))
|
||||
if os.path.exists(test_path):
|
||||
return canonical_filename(test_path)
|
||||
return canonical_filename(abs_path)
|
||||
|
||||
|
||||
class Plugin(CoveragePlugin):
|
||||
# map from traced file paths to absolute file paths
|
||||
_file_path_map = None
|
||||
# map from traced file paths to corresponding C files
|
||||
_c_files_map = None
|
||||
# map from parsed C files to their content
|
||||
_parsed_c_files = None
|
||||
|
||||
def sys_info(self):
|
||||
return [('Cython version', __version__)]
|
||||
|
||||
def file_tracer(self, filename):
|
||||
"""
|
||||
Try to find a C source file for a file path found by the tracer.
|
||||
"""
|
||||
if filename.startswith('<') or filename.startswith('memory:'):
|
||||
return None
|
||||
c_file = py_file = None
|
||||
filename = canonical_filename(os.path.abspath(filename))
|
||||
if self._c_files_map and filename in self._c_files_map:
|
||||
c_file = self._c_files_map[filename][0]
|
||||
|
||||
if c_file is None:
|
||||
c_file, py_file = self._find_source_files(filename)
|
||||
if not c_file:
|
||||
return None # unknown file
|
||||
|
||||
# parse all source file paths and lines from C file
|
||||
# to learn about all relevant source files right away (pyx/pxi/pxd)
|
||||
# FIXME: this might already be too late if the first executed line
|
||||
# is not from the main .pyx file but a file with a different
|
||||
# name than the .c file (which prevents us from finding the
|
||||
# .c file)
|
||||
_, code = self._read_source_lines(c_file, filename)
|
||||
if code is None:
|
||||
return None # no source found
|
||||
|
||||
if self._file_path_map is None:
|
||||
self._file_path_map = {}
|
||||
return CythonModuleTracer(filename, py_file, c_file, self._c_files_map, self._file_path_map)
|
||||
|
||||
def file_reporter(self, filename):
|
||||
# TODO: let coverage.py handle .py files itself
|
||||
#ext = os.path.splitext(filename)[1].lower()
|
||||
#if ext == '.py':
|
||||
# from coverage.python import PythonFileReporter
|
||||
# return PythonFileReporter(filename)
|
||||
|
||||
filename = canonical_filename(os.path.abspath(filename))
|
||||
if self._c_files_map and filename in self._c_files_map:
|
||||
c_file, rel_file_path, code = self._c_files_map[filename]
|
||||
else:
|
||||
c_file, _ = self._find_source_files(filename)
|
||||
if not c_file:
|
||||
return None # unknown file
|
||||
rel_file_path, code = self._read_source_lines(c_file, filename)
|
||||
if code is None:
|
||||
return None # no source found
|
||||
return CythonModuleReporter(c_file, filename, rel_file_path, code)
|
||||
|
||||
def _find_source_files(self, filename):
|
||||
basename, ext = os.path.splitext(filename)
|
||||
ext = ext.lower()
|
||||
if ext in MODULE_FILE_EXTENSIONS:
|
||||
pass
|
||||
elif ext == '.pyd':
|
||||
# Windows extension module
|
||||
platform_suffix = re.search(r'[.]cp[0-9]+-win[_a-z0-9]*$', basename, re.I)
|
||||
if platform_suffix:
|
||||
basename = basename[:platform_suffix.start()]
|
||||
elif ext == '.so':
|
||||
# Linux/Unix/Mac extension module
|
||||
platform_suffix = re.search(r'[.](?:cpython|pypy)-[0-9]+[-_a-z0-9]*$', basename, re.I)
|
||||
if platform_suffix:
|
||||
basename = basename[:platform_suffix.start()]
|
||||
elif ext == '.pxi':
|
||||
# if we get here, it means that the first traced line of a Cython module was
|
||||
# not in the main module but in an include file, so try a little harder to
|
||||
# find the main source file
|
||||
self._find_c_source_files(os.path.dirname(filename), filename)
|
||||
if filename in self._c_files_map:
|
||||
return self._c_files_map[filename][0], None
|
||||
else:
|
||||
# none of our business
|
||||
return None, None
|
||||
|
||||
c_file = filename if ext in C_FILE_EXTENSIONS else _find_c_source(basename)
|
||||
if c_file is None:
|
||||
# a module "pkg/mod.so" can have a source file "pkg/pkg.mod.c"
|
||||
package_root = find_root_package_dir.uncached(filename)
|
||||
package_path = os.path.relpath(basename, package_root).split(os.path.sep)
|
||||
if len(package_path) > 1:
|
||||
test_basepath = os.path.join(os.path.dirname(filename), '.'.join(package_path))
|
||||
c_file = _find_c_source(test_basepath)
|
||||
|
||||
py_source_file = None
|
||||
if c_file:
|
||||
py_source_file = os.path.splitext(c_file)[0] + '.py'
|
||||
if not os.path.exists(py_source_file):
|
||||
py_source_file = None
|
||||
|
||||
try:
|
||||
with open(c_file, 'rb') as f:
|
||||
if b'/* Generated by Cython ' not in f.read(30):
|
||||
return None, None # not a Cython file
|
||||
except (IOError, OSError):
|
||||
c_file = None
|
||||
|
||||
return c_file, py_source_file
|
||||
|
||||
def _find_c_source_files(self, dir_path, source_file):
|
||||
"""
|
||||
Desperately parse all C files in the directory or its package parents
|
||||
(not re-descending) to find the (included) source file in one of them.
|
||||
"""
|
||||
if not os.path.isdir(dir_path):
|
||||
return
|
||||
splitext = os.path.splitext
|
||||
for filename in os.listdir(dir_path):
|
||||
ext = splitext(filename)[1].lower()
|
||||
if ext in C_FILE_EXTENSIONS:
|
||||
self._read_source_lines(os.path.join(dir_path, filename), source_file)
|
||||
if source_file in self._c_files_map:
|
||||
return
|
||||
# not found? then try one package up
|
||||
if is_package_dir(dir_path):
|
||||
self._find_c_source_files(os.path.dirname(dir_path), source_file)
|
||||
|
||||
def _read_source_lines(self, c_file, sourcefile):
|
||||
"""
|
||||
Parse a Cython generated C/C++ source file and find the executable lines.
|
||||
Each executable line starts with a comment header that states source file
|
||||
and line number, as well as the surrounding range of source code lines.
|
||||
"""
|
||||
if self._parsed_c_files is None:
|
||||
self._parsed_c_files = {}
|
||||
if c_file in self._parsed_c_files:
|
||||
code_lines = self._parsed_c_files[c_file]
|
||||
else:
|
||||
code_lines = self._parse_cfile_lines(c_file)
|
||||
self._parsed_c_files[c_file] = code_lines
|
||||
|
||||
if self._c_files_map is None:
|
||||
self._c_files_map = {}
|
||||
|
||||
for filename, code in code_lines.items():
|
||||
abs_path = _find_dep_file_path(c_file, filename,
|
||||
relative_path_search=True)
|
||||
self._c_files_map[abs_path] = (c_file, filename, code)
|
||||
|
||||
if sourcefile not in self._c_files_map:
|
||||
return (None,) * 2 # e.g. shared library file
|
||||
return self._c_files_map[sourcefile][1:]
|
||||
|
||||
def _parse_cfile_lines(self, c_file):
|
||||
"""
|
||||
Parse a C file and extract all source file lines that generated executable code.
|
||||
"""
|
||||
match_source_path_line = re.compile(r' */[*] +"(.*)":([0-9]+)$').match
|
||||
match_current_code_line = re.compile(r' *[*] (.*) # <<<<<<+$').match
|
||||
match_comment_end = re.compile(r' *[*]/$').match
|
||||
match_trace_line = re.compile(r' *__Pyx_TraceLine\(([0-9]+),').match
|
||||
not_executable = re.compile(
|
||||
r'\s*c(?:type)?def\s+'
|
||||
r'(?:(?:public|external)\s+)?'
|
||||
r'(?:struct|union|enum|class)'
|
||||
r'(\s+[^:]+|)\s*:'
|
||||
).match
|
||||
|
||||
code_lines = defaultdict(dict)
|
||||
executable_lines = defaultdict(set)
|
||||
current_filename = None
|
||||
|
||||
with open(c_file) as lines:
|
||||
lines = iter(lines)
|
||||
for line in lines:
|
||||
match = match_source_path_line(line)
|
||||
if not match:
|
||||
if '__Pyx_TraceLine(' in line and current_filename is not None:
|
||||
trace_line = match_trace_line(line)
|
||||
if trace_line:
|
||||
executable_lines[current_filename].add(int(trace_line.group(1)))
|
||||
continue
|
||||
filename, lineno = match.groups()
|
||||
current_filename = filename
|
||||
lineno = int(lineno)
|
||||
for comment_line in lines:
|
||||
match = match_current_code_line(comment_line)
|
||||
if match:
|
||||
code_line = match.group(1).rstrip()
|
||||
if not_executable(code_line):
|
||||
break
|
||||
code_lines[filename][lineno] = code_line
|
||||
break
|
||||
elif match_comment_end(comment_line):
|
||||
# unexpected comment format - false positive?
|
||||
break
|
||||
|
||||
# Remove lines that generated code but are not traceable.
|
||||
for filename, lines in code_lines.items():
|
||||
dead_lines = set(lines).difference(executable_lines.get(filename, ()))
|
||||
for lineno in dead_lines:
|
||||
del lines[lineno]
|
||||
return code_lines
|
||||
|
||||
|
||||
class CythonModuleTracer(FileTracer):
|
||||
"""
|
||||
Find the Python/Cython source file for a Cython module.
|
||||
"""
|
||||
def __init__(self, module_file, py_file, c_file, c_files_map, file_path_map):
|
||||
super(CythonModuleTracer, self).__init__()
|
||||
self.module_file = module_file
|
||||
self.py_file = py_file
|
||||
self.c_file = c_file
|
||||
self._c_files_map = c_files_map
|
||||
self._file_path_map = file_path_map
|
||||
|
||||
def has_dynamic_source_filename(self):
|
||||
return True
|
||||
|
||||
def dynamic_source_filename(self, filename, frame):
|
||||
"""
|
||||
Determine source file path. Called by the function call tracer.
|
||||
"""
|
||||
source_file = frame.f_code.co_filename
|
||||
try:
|
||||
return self._file_path_map[source_file]
|
||||
except KeyError:
|
||||
pass
|
||||
abs_path = _find_dep_file_path(filename, source_file)
|
||||
|
||||
if self.py_file and source_file[-3:].lower() == '.py':
|
||||
# always let coverage.py handle this case itself
|
||||
self._file_path_map[source_file] = self.py_file
|
||||
return self.py_file
|
||||
|
||||
assert self._c_files_map is not None
|
||||
if abs_path not in self._c_files_map:
|
||||
self._c_files_map[abs_path] = (self.c_file, source_file, None)
|
||||
self._file_path_map[source_file] = abs_path
|
||||
return abs_path
|
||||
|
||||
|
||||
class CythonModuleReporter(FileReporter):
|
||||
"""
|
||||
Provide detailed trace information for one source file to coverage.py.
|
||||
"""
|
||||
def __init__(self, c_file, source_file, rel_file_path, code):
|
||||
super(CythonModuleReporter, self).__init__(source_file)
|
||||
self.name = rel_file_path
|
||||
self.c_file = c_file
|
||||
self._code = code
|
||||
|
||||
def lines(self):
|
||||
"""
|
||||
Return set of line numbers that are possibly executable.
|
||||
"""
|
||||
return set(self._code)
|
||||
|
||||
def _iter_source_tokens(self):
|
||||
current_line = 1
|
||||
for line_no, code_line in sorted(self._code.items()):
|
||||
while line_no > current_line:
|
||||
yield []
|
||||
current_line += 1
|
||||
yield [('txt', code_line)]
|
||||
current_line += 1
|
||||
|
||||
def source(self):
|
||||
"""
|
||||
Return the source code of the file as a string.
|
||||
"""
|
||||
if os.path.exists(self.filename):
|
||||
with open_source_file(self.filename) as f:
|
||||
return f.read()
|
||||
else:
|
||||
return '\n'.join(
|
||||
(tokens[0][1] if tokens else '')
|
||||
for tokens in self._iter_source_tokens())
|
||||
|
||||
def source_token_lines(self):
|
||||
"""
|
||||
Iterate over the source code tokens.
|
||||
"""
|
||||
if os.path.exists(self.filename):
|
||||
with open_source_file(self.filename) as f:
|
||||
for line in f:
|
||||
yield [('txt', line.rstrip('\n'))]
|
||||
else:
|
||||
for line in self._iter_source_tokens():
|
||||
yield [('txt', line)]
|
||||
|
||||
|
||||
def coverage_init(reg, options):
|
||||
reg.add_file_tracer(Plugin())
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
The Cython debugger
|
||||
|
||||
The current directory should contain a directory named 'cython_debug', or a
|
||||
path to the cython project directory should be given (the parent directory of
|
||||
cython_debug).
|
||||
|
||||
Additional gdb args can be provided only if a path to the project directory is
|
||||
given.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import tempfile
|
||||
import textwrap
|
||||
import subprocess
|
||||
import optparse
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def make_command_file(path_to_debug_info, prefix_code='', no_import=False):
|
||||
if not no_import:
|
||||
pattern = os.path.join(path_to_debug_info,
|
||||
'cython_debug',
|
||||
'cython_debug_info_*')
|
||||
debug_files = glob.glob(pattern)
|
||||
|
||||
if not debug_files:
|
||||
sys.exit('%s.\nNo debug files were found in %s. Aborting.' % (
|
||||
usage, os.path.abspath(path_to_debug_info)))
|
||||
|
||||
fd, tempfilename = tempfile.mkstemp()
|
||||
f = os.fdopen(fd, 'w')
|
||||
try:
|
||||
f.write(prefix_code)
|
||||
f.write(textwrap.dedent('''\
|
||||
# This is a gdb command file
|
||||
# See https://sourceware.org/gdb/onlinedocs/gdb/Command-Files.html
|
||||
|
||||
set breakpoint pending on
|
||||
set print pretty on
|
||||
|
||||
python
|
||||
# Activate virtualenv, if we were launched from one
|
||||
import os
|
||||
virtualenv = os.getenv('VIRTUAL_ENV')
|
||||
if virtualenv:
|
||||
path_to_activate_this_py = os.path.join(virtualenv, 'bin', 'activate_this.py')
|
||||
print("gdb command file: Activating virtualenv: %s; path_to_activate_this_py: %s" % (
|
||||
virtualenv, path_to_activate_this_py))
|
||||
with open(path_to_activate_this_py) as f:
|
||||
exec(f.read(), dict(__file__=path_to_activate_this_py))
|
||||
|
||||
from Cython.Debugger import libcython, libpython
|
||||
end
|
||||
'''))
|
||||
|
||||
if no_import:
|
||||
# don't do this, this overrides file command in .gdbinit
|
||||
# f.write("file %s\n" % sys.executable)
|
||||
pass
|
||||
else:
|
||||
path = os.path.join(path_to_debug_info, "cython_debug", "interpreter")
|
||||
interpreter_file = open(path)
|
||||
try:
|
||||
interpreter = interpreter_file.read()
|
||||
finally:
|
||||
interpreter_file.close()
|
||||
f.write("file %s\n" % interpreter)
|
||||
f.write('\n'.join('cy import %s\n' % fn for fn in debug_files))
|
||||
f.write(textwrap.dedent('''\
|
||||
python
|
||||
import sys
|
||||
try:
|
||||
gdb.lookup_type('PyModuleObject')
|
||||
except RuntimeError:
|
||||
sys.stderr.write(
|
||||
'Python was not compiled with debug symbols (or it was '
|
||||
'stripped). Some functionality may not work (properly).\\n')
|
||||
end
|
||||
|
||||
source .cygdbinit
|
||||
'''))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
return tempfilename
|
||||
|
||||
usage = "Usage: cygdb [options] [PATH [-- GDB_ARGUMENTS]]"
|
||||
|
||||
def main(path_to_debug_info=None, gdb_argv=None, no_import=False):
|
||||
"""
|
||||
Start the Cython debugger. This tells gdb to import the Cython and Python
|
||||
extensions (libcython.py and libpython.py) and it enables gdb's pending
|
||||
breakpoints.
|
||||
|
||||
path_to_debug_info is the path to the Cython build directory
|
||||
gdb_argv is the list of options to gdb
|
||||
no_import tells cygdb whether it should import debug information
|
||||
"""
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option("--gdb-executable",
|
||||
dest="gdb", default='gdb',
|
||||
help="gdb executable to use [default: gdb]")
|
||||
parser.add_option("--verbose", "-v",
|
||||
dest="verbosity", action="count", default=0,
|
||||
help="Verbose mode. Multiple -v options increase the verbosity")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
if path_to_debug_info is None:
|
||||
if len(args) > 1:
|
||||
path_to_debug_info = args[0]
|
||||
else:
|
||||
path_to_debug_info = os.curdir
|
||||
|
||||
if gdb_argv is None:
|
||||
gdb_argv = args[1:]
|
||||
|
||||
if path_to_debug_info == '--':
|
||||
no_import = True
|
||||
|
||||
logging_level = logging.WARN
|
||||
if options.verbosity == 1:
|
||||
logging_level = logging.INFO
|
||||
if options.verbosity >= 2:
|
||||
logging_level = logging.DEBUG
|
||||
logging.basicConfig(level=logging_level)
|
||||
|
||||
logger.info("verbosity = %r", options.verbosity)
|
||||
logger.debug("options = %r; args = %r", options, args)
|
||||
logger.debug("Done parsing command-line options. path_to_debug_info = %r, gdb_argv = %r",
|
||||
path_to_debug_info, gdb_argv)
|
||||
|
||||
tempfilename = make_command_file(path_to_debug_info, no_import=no_import)
|
||||
logger.info("Launching %s with command file: %s and gdb_argv: %s",
|
||||
options.gdb, tempfilename, gdb_argv)
|
||||
with open(tempfilename) as tempfile:
|
||||
logger.debug('Command file (%s) contains: """\n%s"""', tempfilename, tempfile.read())
|
||||
logger.info("Spawning %s...", options.gdb)
|
||||
p = subprocess.Popen([options.gdb, '-command', tempfilename] + gdb_argv)
|
||||
logger.info("Spawned %s (pid %d)", options.gdb, p.pid)
|
||||
while True:
|
||||
try:
|
||||
logger.debug("Waiting for gdb (pid %d) to exit...", p.pid)
|
||||
ret = p.wait()
|
||||
logger.debug("Wait for gdb (pid %d) to exit is done. Returned: %r", p.pid, ret)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
logger.debug("Closing temp command file with fd: %s", tempfile.fileno())
|
||||
logger.debug("Removing temp command file: %s", tempfilename)
|
||||
os.remove(tempfilename)
|
||||
logger.debug("Removed temp command file: %s", tempfilename)
|
||||
@@ -0,0 +1,72 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
import errno
|
||||
|
||||
try:
|
||||
from lxml import etree
|
||||
have_lxml = True
|
||||
except ImportError:
|
||||
have_lxml = False
|
||||
try:
|
||||
from xml.etree import cElementTree as etree
|
||||
except ImportError:
|
||||
try:
|
||||
from xml.etree import ElementTree as etree
|
||||
except ImportError:
|
||||
etree = None
|
||||
|
||||
from ..Compiler import Errors
|
||||
|
||||
|
||||
class CythonDebugWriter(object):
|
||||
"""
|
||||
Class to output debugging information for cygdb
|
||||
|
||||
It writes debug information to cython_debug/cython_debug_info_<modulename>
|
||||
in the build directory.
|
||||
"""
|
||||
|
||||
def __init__(self, output_dir):
|
||||
if etree is None:
|
||||
raise Errors.NoElementTreeInstalledException()
|
||||
|
||||
self.output_dir = os.path.join(output_dir or os.curdir, 'cython_debug')
|
||||
self.tb = etree.TreeBuilder()
|
||||
# set by Cython.Compiler.ParseTreeTransforms.DebugTransform
|
||||
self.module_name = None
|
||||
self.start('cython_debug', attrs=dict(version='1.0'))
|
||||
|
||||
def start(self, name, attrs=None):
|
||||
self.tb.start(name, attrs or {})
|
||||
|
||||
def end(self, name):
|
||||
self.tb.end(name)
|
||||
|
||||
def add_entry(self, name, **attrs):
|
||||
self.tb.start(name, attrs)
|
||||
self.tb.end(name)
|
||||
|
||||
def serialize(self):
|
||||
self.tb.end('Module')
|
||||
self.tb.end('cython_debug')
|
||||
xml_root_element = self.tb.close()
|
||||
|
||||
try:
|
||||
os.makedirs(self.output_dir)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise
|
||||
|
||||
et = etree.ElementTree(xml_root_element)
|
||||
kw = {}
|
||||
if have_lxml:
|
||||
kw['pretty_print'] = True
|
||||
|
||||
fn = "cython_debug_info_" + self.module_name
|
||||
et.write(os.path.join(self.output_dir, fn), encoding="UTF-8", **kw)
|
||||
|
||||
interpreter_path = os.path.join(self.output_dir, 'interpreter')
|
||||
with open(interpreter_path, 'w') as f:
|
||||
f.write(sys.executable)
|
||||
@@ -0,0 +1,274 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import shutil
|
||||
import warnings
|
||||
import textwrap
|
||||
import unittest
|
||||
import tempfile
|
||||
import subprocess
|
||||
#import distutils.core
|
||||
#from distutils import sysconfig
|
||||
from distutils import ccompiler
|
||||
|
||||
import runtests
|
||||
import Cython.Distutils.extension
|
||||
import Cython.Distutils.old_build_ext as build_ext
|
||||
from Cython.Debugger import Cygdb as cygdb
|
||||
|
||||
root = os.path.dirname(os.path.abspath(__file__))
|
||||
codefile = os.path.join(root, 'codefile')
|
||||
cfuncs_file = os.path.join(root, 'cfuncs.c')
|
||||
|
||||
with open(codefile) as f:
|
||||
source_to_lineno = dict((line.strip(), i + 1) for i, line in enumerate(f))
|
||||
|
||||
|
||||
have_gdb = None
|
||||
def test_gdb():
|
||||
global have_gdb
|
||||
if have_gdb is not None:
|
||||
return have_gdb
|
||||
|
||||
have_gdb = False
|
||||
try:
|
||||
p = subprocess.Popen(['gdb', '-nx', '--version'], stdout=subprocess.PIPE)
|
||||
except OSError:
|
||||
# gdb not found
|
||||
gdb_version = None
|
||||
else:
|
||||
stdout, _ = p.communicate()
|
||||
# Based on Lib/test/test_gdb.py
|
||||
regex = r"GNU gdb [^\d]*(\d+)\.(\d+)"
|
||||
gdb_version = re.match(regex, stdout.decode('ascii', 'ignore'))
|
||||
|
||||
if gdb_version:
|
||||
gdb_version_number = list(map(int, gdb_version.groups()))
|
||||
if gdb_version_number >= [7, 2]:
|
||||
have_gdb = True
|
||||
with tempfile.NamedTemporaryFile(mode='w+') as python_version_script:
|
||||
python_version_script.write(
|
||||
'python import sys; print("%s %s" % sys.version_info[:2])')
|
||||
python_version_script.flush()
|
||||
p = subprocess.Popen(['gdb', '-batch', '-x', python_version_script.name],
|
||||
stdout=subprocess.PIPE)
|
||||
stdout, _ = p.communicate()
|
||||
try:
|
||||
internal_python_version = list(map(int, stdout.decode('ascii', 'ignore').split()))
|
||||
if internal_python_version < [2, 6]:
|
||||
have_gdb = False
|
||||
except ValueError:
|
||||
have_gdb = False
|
||||
|
||||
if not have_gdb:
|
||||
warnings.warn('Skipping gdb tests, need gdb >= 7.2 with Python >= 2.6')
|
||||
|
||||
return have_gdb
|
||||
|
||||
|
||||
class DebuggerTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Run gdb and have cygdb import the debug information from the code
|
||||
defined in TestParseTreeTransforms's setUp method
|
||||
"""
|
||||
if not test_gdb():
|
||||
return
|
||||
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.destfile = os.path.join(self.tempdir, 'codefile.pyx')
|
||||
self.debug_dest = os.path.join(self.tempdir,
|
||||
'cython_debug',
|
||||
'cython_debug_info_codefile')
|
||||
self.cfuncs_destfile = os.path.join(self.tempdir, 'cfuncs')
|
||||
|
||||
self.cwd = os.getcwd()
|
||||
try:
|
||||
os.chdir(self.tempdir)
|
||||
|
||||
shutil.copy(codefile, self.destfile)
|
||||
shutil.copy(cfuncs_file, self.cfuncs_destfile + '.c')
|
||||
shutil.copy(cfuncs_file.replace('.c', '.h'),
|
||||
self.cfuncs_destfile + '.h')
|
||||
|
||||
compiler = ccompiler.new_compiler()
|
||||
compiler.compile(['cfuncs.c'], debug=True, extra_postargs=['-fPIC'])
|
||||
|
||||
opts = dict(
|
||||
test_directory=self.tempdir,
|
||||
module='codefile',
|
||||
)
|
||||
|
||||
optimization_disabler = build_ext.Optimization()
|
||||
|
||||
cython_compile_testcase = runtests.CythonCompileTestCase(
|
||||
workdir=self.tempdir,
|
||||
# we clean up everything (not only compiled files)
|
||||
cleanup_workdir=False,
|
||||
tags=runtests.parse_tags(codefile),
|
||||
**opts
|
||||
)
|
||||
|
||||
|
||||
new_stderr = open(os.devnull, 'w')
|
||||
|
||||
stderr = sys.stderr
|
||||
sys.stderr = new_stderr
|
||||
|
||||
optimization_disabler.disable_optimization()
|
||||
try:
|
||||
cython_compile_testcase.run_cython(
|
||||
targetdir=self.tempdir,
|
||||
incdir=None,
|
||||
annotate=False,
|
||||
extra_compile_options={
|
||||
'gdb_debug':True,
|
||||
'output_dir':self.tempdir,
|
||||
},
|
||||
**opts
|
||||
)
|
||||
|
||||
cython_compile_testcase.run_distutils(
|
||||
incdir=None,
|
||||
workdir=self.tempdir,
|
||||
extra_extension_args={'extra_objects':['cfuncs.o']},
|
||||
**opts
|
||||
)
|
||||
finally:
|
||||
optimization_disabler.restore_state()
|
||||
sys.stderr = stderr
|
||||
new_stderr.close()
|
||||
|
||||
# ext = Cython.Distutils.extension.Extension(
|
||||
# 'codefile',
|
||||
# ['codefile.pyx'],
|
||||
# cython_gdb=True,
|
||||
# extra_objects=['cfuncs.o'])
|
||||
#
|
||||
# distutils.core.setup(
|
||||
# script_args=['build_ext', '--inplace'],
|
||||
# ext_modules=[ext],
|
||||
# cmdclass=dict(build_ext=Cython.Distutils.build_ext)
|
||||
# )
|
||||
|
||||
except:
|
||||
os.chdir(self.cwd)
|
||||
raise
|
||||
|
||||
def tearDown(self):
|
||||
if not test_gdb():
|
||||
return
|
||||
os.chdir(self.cwd)
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
|
||||
class GdbDebuggerTestCase(DebuggerTestCase):
|
||||
|
||||
def setUp(self):
|
||||
if not test_gdb():
|
||||
return
|
||||
|
||||
super(GdbDebuggerTestCase, self).setUp()
|
||||
|
||||
prefix_code = textwrap.dedent('''\
|
||||
python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
def excepthook(type, value, tb):
|
||||
traceback.print_exception(type, value, tb)
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
os._exit(1)
|
||||
|
||||
sys.excepthook = excepthook
|
||||
|
||||
# Have tracebacks end up on sys.stderr (gdb replaces sys.stderr
|
||||
# with an object that calls gdb.write())
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
end
|
||||
''')
|
||||
|
||||
code = textwrap.dedent('''\
|
||||
python
|
||||
|
||||
from Cython.Debugger.Tests import test_libcython_in_gdb
|
||||
test_libcython_in_gdb.main(version=%r)
|
||||
|
||||
end
|
||||
''' % (sys.version_info[:2],))
|
||||
|
||||
self.gdb_command_file = cygdb.make_command_file(self.tempdir,
|
||||
prefix_code)
|
||||
|
||||
with open(self.gdb_command_file, 'a') as f:
|
||||
f.write(code)
|
||||
|
||||
args = ['gdb', '-batch', '-x', self.gdb_command_file, '-n', '--args',
|
||||
sys.executable, '-c', 'import codefile']
|
||||
|
||||
paths = []
|
||||
path = os.environ.get('PYTHONPATH')
|
||||
if path:
|
||||
paths.append(path)
|
||||
paths.append(os.path.dirname(os.path.dirname(
|
||||
os.path.abspath(Cython.__file__))))
|
||||
env = dict(os.environ, PYTHONPATH=os.pathsep.join(paths))
|
||||
|
||||
self.p = subprocess.Popen(
|
||||
args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
env=env)
|
||||
|
||||
def tearDown(self):
|
||||
if not test_gdb():
|
||||
return
|
||||
|
||||
try:
|
||||
super(GdbDebuggerTestCase, self).tearDown()
|
||||
if self.p:
|
||||
try: self.p.stdout.close()
|
||||
except: pass
|
||||
try: self.p.stderr.close()
|
||||
except: pass
|
||||
self.p.wait()
|
||||
finally:
|
||||
os.remove(self.gdb_command_file)
|
||||
|
||||
|
||||
class TestAll(GdbDebuggerTestCase):
|
||||
|
||||
def test_all(self):
|
||||
if not test_gdb():
|
||||
return
|
||||
|
||||
out, err = self.p.communicate()
|
||||
out = out.decode('UTF-8')
|
||||
err = err.decode('UTF-8')
|
||||
|
||||
exit_status = self.p.returncode
|
||||
|
||||
if exit_status == 1:
|
||||
sys.stderr.write(out)
|
||||
sys.stderr.write(err)
|
||||
elif exit_status >= 2:
|
||||
border = u'*' * 30
|
||||
start = u'%s v INSIDE GDB v %s' % (border, border)
|
||||
stderr = u'%s v STDERR v %s' % (border, border)
|
||||
end = u'%s ^ INSIDE GDB ^ %s' % (border, border)
|
||||
errmsg = u'\n%s\n%s%s\n%s%s' % (start, out, stderr, err, end)
|
||||
|
||||
sys.stderr.write(errmsg)
|
||||
|
||||
# FIXME: re-enable this to make the test fail on internal failures
|
||||
#self.assertEqual(exit_status, 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1 @@
|
||||
# empty file
|
||||
@@ -0,0 +1,8 @@
|
||||
void
|
||||
some_c_function(void)
|
||||
{
|
||||
int a, b, c;
|
||||
|
||||
a = 1;
|
||||
b = 2;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
cdef extern from "stdio.h":
|
||||
int puts(char *s)
|
||||
|
||||
cdef extern from "cfuncs.h":
|
||||
void some_c_function()
|
||||
|
||||
import os
|
||||
|
||||
cdef int c_var = 12
|
||||
python_var = 13
|
||||
|
||||
def spam(a=0):
|
||||
cdef:
|
||||
int b, c
|
||||
|
||||
b = c = d = 0
|
||||
|
||||
b = 1
|
||||
c = 2
|
||||
int(10)
|
||||
puts("spam")
|
||||
os.path.join("foo", "bar")
|
||||
some_c_function()
|
||||
|
||||
cpdef eggs():
|
||||
pass
|
||||
|
||||
cdef ham():
|
||||
pass
|
||||
|
||||
cdef class SomeClass(object):
|
||||
def spam(self):
|
||||
pass
|
||||
|
||||
def outer():
|
||||
cdef object a = "an object"
|
||||
def inner():
|
||||
b = 2
|
||||
# access closed over variables
|
||||
print a, b
|
||||
return inner
|
||||
|
||||
|
||||
outer()()
|
||||
|
||||
spam()
|
||||
print "bye!"
|
||||
|
||||
def use_ham():
|
||||
ham()
|
||||
@@ -0,0 +1,496 @@
|
||||
"""
|
||||
Tests that run inside GDB.
|
||||
|
||||
Note: debug information is already imported by the file generated by
|
||||
Cython.Debugger.Cygdb.make_command_file()
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import trace
|
||||
import inspect
|
||||
import warnings
|
||||
import unittest
|
||||
import textwrap
|
||||
import tempfile
|
||||
import functools
|
||||
import traceback
|
||||
import itertools
|
||||
#from test import test_support
|
||||
|
||||
import gdb
|
||||
|
||||
from .. import libcython
|
||||
from .. import libpython
|
||||
from . import TestLibCython as test_libcython
|
||||
from ...Utils import add_metaclass
|
||||
|
||||
# for some reason sys.argv is missing in gdb
|
||||
sys.argv = ['gdb']
|
||||
|
||||
|
||||
def print_on_call_decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
_debug(type(self).__name__, func.__name__)
|
||||
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except Exception:
|
||||
_debug("An exception occurred:", traceback.format_exc())
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
class TraceMethodCallMeta(type):
|
||||
|
||||
def __init__(self, name, bases, dict):
|
||||
for func_name, func in dict.items():
|
||||
if inspect.isfunction(func):
|
||||
setattr(self, func_name, print_on_call_decorator(func))
|
||||
|
||||
|
||||
@add_metaclass(TraceMethodCallMeta)
|
||||
class DebugTestCase(unittest.TestCase):
|
||||
"""
|
||||
Base class for test cases. On teardown it kills the inferior and unsets
|
||||
all breakpoints.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
super(DebugTestCase, self).__init__(name)
|
||||
self.cy = libcython.cy
|
||||
self.module = libcython.cy.cython_namespace['codefile']
|
||||
self.spam_func, self.spam_meth = libcython.cy.functions_by_name['spam']
|
||||
self.ham_func = libcython.cy.functions_by_qualified_name[
|
||||
'codefile.ham']
|
||||
self.eggs_func = libcython.cy.functions_by_qualified_name[
|
||||
'codefile.eggs']
|
||||
|
||||
def read_var(self, varname, cast_to=None):
|
||||
result = gdb.parse_and_eval('$cy_cvalue("%s")' % varname)
|
||||
if cast_to:
|
||||
result = cast_to(result)
|
||||
|
||||
return result
|
||||
|
||||
def local_info(self):
|
||||
return gdb.execute('info locals', to_string=True)
|
||||
|
||||
def lineno_equals(self, source_line=None, lineno=None):
|
||||
if source_line is not None:
|
||||
lineno = test_libcython.source_to_lineno[source_line]
|
||||
frame = gdb.selected_frame()
|
||||
self.assertEqual(libcython.cython_info.lineno(frame), lineno)
|
||||
|
||||
def break_and_run(self, source_line):
|
||||
break_lineno = test_libcython.source_to_lineno[source_line]
|
||||
gdb.execute('cy break codefile:%d' % break_lineno, to_string=True)
|
||||
gdb.execute('run', to_string=True)
|
||||
|
||||
def tearDown(self):
|
||||
gdb.execute('delete breakpoints', to_string=True)
|
||||
try:
|
||||
gdb.execute('kill inferior 1', to_string=True)
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
gdb.execute('set args -c "import codefile"')
|
||||
|
||||
|
||||
class TestDebugInformationClasses(DebugTestCase):
|
||||
|
||||
def test_CythonModule(self):
|
||||
"test that debug information was parsed properly into data structures"
|
||||
self.assertEqual(self.module.name, 'codefile')
|
||||
global_vars = ('c_var', 'python_var', '__name__',
|
||||
'__builtins__', '__doc__', '__file__')
|
||||
assert set(global_vars).issubset(self.module.globals)
|
||||
|
||||
def test_CythonVariable(self):
|
||||
module_globals = self.module.globals
|
||||
c_var = module_globals['c_var']
|
||||
python_var = module_globals['python_var']
|
||||
self.assertEqual(c_var.type, libcython.CObject)
|
||||
self.assertEqual(python_var.type, libcython.PythonObject)
|
||||
self.assertEqual(c_var.qualified_name, 'codefile.c_var')
|
||||
|
||||
def test_CythonFunction(self):
|
||||
self.assertEqual(self.spam_func.qualified_name, 'codefile.spam')
|
||||
self.assertEqual(self.spam_meth.qualified_name,
|
||||
'codefile.SomeClass.spam')
|
||||
self.assertEqual(self.spam_func.module, self.module)
|
||||
|
||||
assert self.eggs_func.pf_cname, (self.eggs_func, self.eggs_func.pf_cname)
|
||||
assert not self.ham_func.pf_cname
|
||||
assert not self.spam_func.pf_cname
|
||||
assert not self.spam_meth.pf_cname
|
||||
|
||||
self.assertEqual(self.spam_func.type, libcython.CObject)
|
||||
self.assertEqual(self.ham_func.type, libcython.CObject)
|
||||
|
||||
self.assertEqual(self.spam_func.arguments, ['a'])
|
||||
self.assertEqual(self.spam_func.step_into_functions,
|
||||
set(['puts', 'some_c_function']))
|
||||
|
||||
expected_lineno = test_libcython.source_to_lineno['def spam(a=0):']
|
||||
self.assertEqual(self.spam_func.lineno, expected_lineno)
|
||||
self.assertEqual(sorted(self.spam_func.locals), list('abcd'))
|
||||
|
||||
|
||||
class TestParameters(unittest.TestCase):
|
||||
|
||||
def test_parameters(self):
|
||||
gdb.execute('set cy_colorize_code on')
|
||||
assert libcython.parameters.colorize_code
|
||||
gdb.execute('set cy_colorize_code off')
|
||||
assert not libcython.parameters.colorize_code
|
||||
|
||||
|
||||
class TestBreak(DebugTestCase):
|
||||
|
||||
def test_break(self):
|
||||
breakpoint_amount = len(gdb.breakpoints() or ())
|
||||
gdb.execute('cy break codefile.spam')
|
||||
|
||||
self.assertEqual(len(gdb.breakpoints()), breakpoint_amount + 1)
|
||||
bp = gdb.breakpoints()[-1]
|
||||
self.assertEqual(bp.type, gdb.BP_BREAKPOINT)
|
||||
assert self.spam_func.cname in bp.location
|
||||
assert bp.enabled
|
||||
|
||||
def test_python_break(self):
|
||||
gdb.execute('cy break -p join')
|
||||
assert 'def join(' in gdb.execute('cy run', to_string=True)
|
||||
|
||||
def test_break_lineno(self):
|
||||
beginline = 'import os'
|
||||
nextline = 'cdef int c_var = 12'
|
||||
|
||||
self.break_and_run(beginline)
|
||||
self.lineno_equals(beginline)
|
||||
step_result = gdb.execute('cy step', to_string=True)
|
||||
self.lineno_equals(nextline)
|
||||
assert step_result.rstrip().endswith(nextline)
|
||||
|
||||
|
||||
class TestKilled(DebugTestCase):
|
||||
|
||||
def test_abort(self):
|
||||
gdb.execute("set args -c 'import os; os.abort()'")
|
||||
output = gdb.execute('cy run', to_string=True)
|
||||
assert 'abort' in output.lower()
|
||||
|
||||
|
||||
class DebugStepperTestCase(DebugTestCase):
|
||||
|
||||
def step(self, varnames_and_values, source_line=None, lineno=None):
|
||||
gdb.execute(self.command)
|
||||
for varname, value in varnames_and_values:
|
||||
self.assertEqual(self.read_var(varname), value, self.local_info())
|
||||
|
||||
self.lineno_equals(source_line, lineno)
|
||||
|
||||
|
||||
class TestStep(DebugStepperTestCase):
|
||||
"""
|
||||
Test stepping. Stepping happens in the code found in
|
||||
Cython/Debugger/Tests/codefile.
|
||||
"""
|
||||
|
||||
def test_cython_step(self):
|
||||
gdb.execute('cy break codefile.spam')
|
||||
|
||||
gdb.execute('run', to_string=True)
|
||||
self.lineno_equals('def spam(a=0):')
|
||||
|
||||
gdb.execute('cy step', to_string=True)
|
||||
self.lineno_equals('b = c = d = 0')
|
||||
|
||||
self.command = 'cy step'
|
||||
self.step([('b', 0)], source_line='b = 1')
|
||||
self.step([('b', 1), ('c', 0)], source_line='c = 2')
|
||||
self.step([('c', 2)], source_line='int(10)')
|
||||
self.step([], source_line='puts("spam")')
|
||||
|
||||
gdb.execute('cont', to_string=True)
|
||||
self.assertEqual(len(gdb.inferiors()), 1)
|
||||
self.assertEqual(gdb.inferiors()[0].pid, 0)
|
||||
|
||||
def test_c_step(self):
|
||||
self.break_and_run('some_c_function()')
|
||||
gdb.execute('cy step', to_string=True)
|
||||
self.assertEqual(gdb.selected_frame().name(), 'some_c_function')
|
||||
|
||||
def test_python_step(self):
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
|
||||
result = gdb.execute('cy step', to_string=True)
|
||||
|
||||
curframe = gdb.selected_frame()
|
||||
self.assertEqual(curframe.name(), 'PyEval_EvalFrameEx')
|
||||
|
||||
pyframe = libpython.Frame(curframe).get_pyop()
|
||||
# With Python 3 inferiors, pyframe.co_name will return a PyUnicodePtr,
|
||||
# be compatible
|
||||
frame_name = pyframe.co_name.proxyval(set())
|
||||
self.assertEqual(frame_name, 'join')
|
||||
assert re.match(r'\d+ def join\(', result), result
|
||||
|
||||
|
||||
class TestNext(DebugStepperTestCase):
|
||||
|
||||
def test_cython_next(self):
|
||||
self.break_and_run('c = 2')
|
||||
|
||||
lines = (
|
||||
'int(10)',
|
||||
'puts("spam")',
|
||||
'os.path.join("foo", "bar")',
|
||||
'some_c_function()',
|
||||
)
|
||||
|
||||
for line in lines:
|
||||
gdb.execute('cy next')
|
||||
self.lineno_equals(line)
|
||||
|
||||
|
||||
class TestLocalsGlobals(DebugTestCase):
|
||||
|
||||
def test_locals(self):
|
||||
self.break_and_run('int(10)')
|
||||
|
||||
result = gdb.execute('cy locals', to_string=True)
|
||||
assert 'a = 0', repr(result)
|
||||
assert 'b = (int) 1', result
|
||||
assert 'c = (int) 2' in result, repr(result)
|
||||
|
||||
def test_globals(self):
|
||||
self.break_and_run('int(10)')
|
||||
|
||||
result = gdb.execute('cy globals', to_string=True)
|
||||
assert '__name__ ' in result, repr(result)
|
||||
assert '__doc__ ' in result, repr(result)
|
||||
assert 'os ' in result, repr(result)
|
||||
assert 'c_var ' in result, repr(result)
|
||||
assert 'python_var ' in result, repr(result)
|
||||
|
||||
|
||||
class TestBacktrace(DebugTestCase):
|
||||
|
||||
def test_backtrace(self):
|
||||
libcython.parameters.colorize_code.value = False
|
||||
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
|
||||
def match_backtrace_output(result):
|
||||
assert re.search(r'\#\d+ *0x.* in spam\(\) at .*codefile\.pyx:22',
|
||||
result), result
|
||||
assert 'os.path.join("foo", "bar")' in result, result
|
||||
|
||||
result = gdb.execute('cy bt', to_string=True)
|
||||
match_backtrace_output(result)
|
||||
|
||||
result = gdb.execute('cy bt -a', to_string=True)
|
||||
match_backtrace_output(result)
|
||||
|
||||
# Apparently not everyone has main()
|
||||
# assert re.search(r'\#0 *0x.* in main\(\)', result), result
|
||||
|
||||
|
||||
class TestFunctions(DebugTestCase):
|
||||
|
||||
def test_functions(self):
|
||||
self.break_and_run('c = 2')
|
||||
result = gdb.execute('print $cy_cname("b")', to_string=True)
|
||||
assert re.search('__pyx_.*b', result), result
|
||||
|
||||
result = gdb.execute('print $cy_lineno()', to_string=True)
|
||||
supposed_lineno = test_libcython.source_to_lineno['c = 2']
|
||||
assert str(supposed_lineno) in result, (supposed_lineno, result)
|
||||
|
||||
result = gdb.execute('print $cy_cvalue("b")', to_string=True)
|
||||
assert '= 1' in result
|
||||
|
||||
|
||||
class TestPrint(DebugTestCase):
|
||||
|
||||
def test_print(self):
|
||||
self.break_and_run('c = 2')
|
||||
result = gdb.execute('cy print b', to_string=True)
|
||||
self.assertEqual('b = (int) 1\n', result)
|
||||
|
||||
|
||||
class TestUpDown(DebugTestCase):
|
||||
|
||||
def test_updown(self):
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
gdb.execute('cy step')
|
||||
self.assertRaises(RuntimeError, gdb.execute, 'cy down')
|
||||
|
||||
result = gdb.execute('cy up', to_string=True)
|
||||
assert 'spam()' in result
|
||||
assert 'os.path.join("foo", "bar")' in result
|
||||
|
||||
|
||||
class TestExec(DebugTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestExec, self).setUp()
|
||||
self.fd, self.tmpfilename = tempfile.mkstemp()
|
||||
self.tmpfile = os.fdopen(self.fd, 'r+')
|
||||
|
||||
def tearDown(self):
|
||||
super(TestExec, self).tearDown()
|
||||
|
||||
try:
|
||||
self.tmpfile.close()
|
||||
finally:
|
||||
os.remove(self.tmpfilename)
|
||||
|
||||
def eval_command(self, command):
|
||||
gdb.execute('cy exec open(%r, "w").write(str(%s))' %
|
||||
(self.tmpfilename, command))
|
||||
return self.tmpfile.read().strip()
|
||||
|
||||
def test_cython_exec(self):
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
|
||||
# test normal behaviour
|
||||
self.assertEqual("[0]", self.eval_command('[a]'))
|
||||
|
||||
# test multiline code
|
||||
result = gdb.execute(textwrap.dedent('''\
|
||||
cy exec
|
||||
pass
|
||||
|
||||
"nothing"
|
||||
end
|
||||
'''))
|
||||
result = self.tmpfile.read().rstrip()
|
||||
self.assertEqual('', result)
|
||||
|
||||
def test_python_exec(self):
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
gdb.execute('cy step')
|
||||
|
||||
gdb.execute('cy exec some_random_var = 14')
|
||||
self.assertEqual('14', self.eval_command('some_random_var'))
|
||||
|
||||
|
||||
class CySet(DebugTestCase):
|
||||
|
||||
def test_cyset(self):
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
|
||||
gdb.execute('cy set a = $cy_eval("{None: []}")')
|
||||
stringvalue = self.read_var("a", cast_to=str)
|
||||
self.assertEqual(stringvalue, "{None: []}")
|
||||
|
||||
|
||||
class TestCyEval(DebugTestCase):
|
||||
"Test the $cy_eval() gdb function."
|
||||
|
||||
def test_cy_eval(self):
|
||||
# This function leaks a few objects in the GDB python process. This
|
||||
# is no biggie
|
||||
self.break_and_run('os.path.join("foo", "bar")')
|
||||
|
||||
result = gdb.execute('print $cy_eval("None")', to_string=True)
|
||||
assert re.match(r'\$\d+ = None\n', result), result
|
||||
|
||||
result = gdb.execute('print $cy_eval("[a]")', to_string=True)
|
||||
assert re.match(r'\$\d+ = \[0\]', result), result
|
||||
|
||||
|
||||
class TestClosure(DebugTestCase):
|
||||
|
||||
def break_and_run_func(self, funcname):
|
||||
gdb.execute('cy break ' + funcname)
|
||||
gdb.execute('cy run')
|
||||
|
||||
def test_inner(self):
|
||||
self.break_and_run_func('inner')
|
||||
self.assertEqual('', gdb.execute('cy locals', to_string=True))
|
||||
|
||||
# Allow the Cython-generated code to initialize the scope variable
|
||||
gdb.execute('cy step')
|
||||
|
||||
self.assertEqual(str(self.read_var('a')), "'an object'")
|
||||
print_result = gdb.execute('cy print a', to_string=True).strip()
|
||||
self.assertEqual(print_result, "a = 'an object'")
|
||||
|
||||
def test_outer(self):
|
||||
self.break_and_run_func('outer')
|
||||
self.assertEqual('', gdb.execute('cy locals', to_string=True))
|
||||
|
||||
# Initialize scope with 'a' uninitialized
|
||||
gdb.execute('cy step')
|
||||
self.assertEqual('', gdb.execute('cy locals', to_string=True))
|
||||
|
||||
# Initialize 'a' to 1
|
||||
gdb.execute('cy step')
|
||||
print_result = gdb.execute('cy print a', to_string=True).strip()
|
||||
self.assertEqual(print_result, "a = 'an object'")
|
||||
|
||||
|
||||
_do_debug = os.environ.get('GDB_DEBUG')
|
||||
if _do_debug:
|
||||
_debug_file = open('/dev/tty', 'w')
|
||||
|
||||
def _debug(*messages):
|
||||
if _do_debug:
|
||||
messages = itertools.chain([sys._getframe(1).f_code.co_name, ':'],
|
||||
messages)
|
||||
_debug_file.write(' '.join(str(msg) for msg in messages) + '\n')
|
||||
|
||||
|
||||
def run_unittest_in_module(modulename):
|
||||
try:
|
||||
gdb.lookup_type('PyModuleObject')
|
||||
except RuntimeError:
|
||||
msg = ("Unable to run tests, Python was not compiled with "
|
||||
"debugging information. Either compile python with "
|
||||
"-g or get a debug build (configure with --with-pydebug).")
|
||||
warnings.warn(msg)
|
||||
os._exit(1)
|
||||
else:
|
||||
m = __import__(modulename, fromlist=[''])
|
||||
tests = inspect.getmembers(m, inspect.isclass)
|
||||
|
||||
# test_support.run_unittest(tests)
|
||||
|
||||
test_loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite(
|
||||
[test_loader.loadTestsFromTestCase(cls) for name, cls in tests])
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=1).run(suite)
|
||||
return result.wasSuccessful()
|
||||
|
||||
def runtests():
|
||||
"""
|
||||
Run the libcython and libpython tests. Ensure that an appropriate status is
|
||||
returned to the parent test process.
|
||||
"""
|
||||
from Cython.Debugger.Tests import test_libpython_in_gdb
|
||||
|
||||
success_libcython = run_unittest_in_module(__name__)
|
||||
success_libpython = run_unittest_in_module(test_libpython_in_gdb.__name__)
|
||||
|
||||
if not success_libcython or not success_libpython:
|
||||
sys.exit(2)
|
||||
|
||||
def main(version, trace_code=False):
|
||||
global inferior_python_version
|
||||
|
||||
inferior_python_version = version
|
||||
|
||||
if trace_code:
|
||||
tracer = trace.Trace(count=False, trace=True, outfile=sys.stderr,
|
||||
ignoredirs=[sys.prefix, sys.exec_prefix])
|
||||
tracer.runfunc(runtests)
|
||||
else:
|
||||
runtests()
|
||||
@@ -0,0 +1,115 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
"""
|
||||
Test libpython.py. This is already partly tested by test_libcython_in_gdb and
|
||||
Lib/test/test_gdb.py in the Python source. These tests are run in gdb and
|
||||
called from test_libcython_in_gdb.main()
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gdb
|
||||
|
||||
from Cython.Debugger import libcython
|
||||
from Cython.Debugger import libpython
|
||||
|
||||
from . import test_libcython_in_gdb
|
||||
from .test_libcython_in_gdb import _debug, inferior_python_version
|
||||
|
||||
|
||||
class TestPrettyPrinters(test_libcython_in_gdb.DebugTestCase):
|
||||
"""
|
||||
Test whether types of Python objects are correctly inferred and that
|
||||
the right libpython.PySomeTypeObjectPtr classes are instantiated.
|
||||
|
||||
Also test whether values are appropriately formatted (don't be too
|
||||
laborious as Lib/test/test_gdb.py already covers this extensively).
|
||||
|
||||
Don't take care of decreffing newly allocated objects as a new
|
||||
interpreter is started for every test anyway.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestPrettyPrinters, self).setUp()
|
||||
self.break_and_run('b = c = d = 0')
|
||||
|
||||
def get_pyobject(self, code):
|
||||
value = gdb.parse_and_eval(code)
|
||||
assert libpython.pointervalue(value) != 0
|
||||
return value
|
||||
|
||||
def pyobject_fromcode(self, code, gdbvar=None):
|
||||
if gdbvar is not None:
|
||||
d = {'varname':gdbvar, 'code':code}
|
||||
gdb.execute('set $%(varname)s = %(code)s' % d)
|
||||
code = '$' + gdbvar
|
||||
|
||||
return libpython.PyObjectPtr.from_pyobject_ptr(self.get_pyobject(code))
|
||||
|
||||
def get_repr(self, pyobject):
|
||||
return pyobject.get_truncated_repr(libpython.MAX_OUTPUT_LEN)
|
||||
|
||||
def alloc_bytestring(self, string, gdbvar=None):
|
||||
if inferior_python_version < (3, 0):
|
||||
funcname = 'PyString_FromStringAndSize'
|
||||
else:
|
||||
funcname = 'PyBytes_FromStringAndSize'
|
||||
|
||||
assert b'"' not in string
|
||||
|
||||
# ensure double quotes
|
||||
code = '(PyObject *) %s("%s", %d)' % (funcname, string.decode('iso8859-1'), len(string))
|
||||
return self.pyobject_fromcode(code, gdbvar=gdbvar)
|
||||
|
||||
def alloc_unicodestring(self, string, gdbvar=None):
|
||||
postfix = libpython.get_inferior_unicode_postfix()
|
||||
funcname = 'PyUnicode%s_DecodeUnicodeEscape' % (postfix,)
|
||||
|
||||
data = string.encode("unicode_escape").decode('iso8859-1')
|
||||
return self.pyobject_fromcode(
|
||||
'(PyObject *) %s("%s", %d, "strict")' % (
|
||||
funcname, data.replace('"', r'\"').replace('\\', r'\\'), len(data)),
|
||||
gdbvar=gdbvar)
|
||||
|
||||
def test_bytestring(self):
|
||||
bytestring = self.alloc_bytestring(b"spam")
|
||||
|
||||
if inferior_python_version < (3, 0):
|
||||
bytestring_class = libpython.PyStringObjectPtr
|
||||
expected = repr(b"spam")
|
||||
else:
|
||||
bytestring_class = libpython.PyBytesObjectPtr
|
||||
expected = "b'spam'"
|
||||
|
||||
self.assertEqual(type(bytestring), bytestring_class)
|
||||
self.assertEqual(self.get_repr(bytestring), expected)
|
||||
|
||||
def test_unicode(self):
|
||||
unicode_string = self.alloc_unicodestring(u"spam ἄλφα")
|
||||
|
||||
expected = u"'spam ἄλφα'"
|
||||
if inferior_python_version < (3, 0):
|
||||
expected = 'u' + expected
|
||||
|
||||
self.assertEqual(type(unicode_string), libpython.PyUnicodeObjectPtr)
|
||||
self.assertEqual(self.get_repr(unicode_string), expected)
|
||||
|
||||
def test_int(self):
|
||||
if inferior_python_version < (3, 0):
|
||||
intval = self.pyobject_fromcode('PyInt_FromLong(100)')
|
||||
self.assertEqual(type(intval), libpython.PyIntObjectPtr)
|
||||
self.assertEqual(self.get_repr(intval), '100')
|
||||
|
||||
def test_long(self):
|
||||
longval = self.pyobject_fromcode('PyLong_FromLong(200)',
|
||||
gdbvar='longval')
|
||||
assert gdb.parse_and_eval('$longval->ob_type == &PyLong_Type')
|
||||
|
||||
self.assertEqual(type(longval), libpython.PyLongObjectPtr)
|
||||
self.assertEqual(self.get_repr(longval), '200')
|
||||
|
||||
def test_frame_type(self):
|
||||
frame = self.pyobject_fromcode('PyEval_GetFrame()')
|
||||
|
||||
self.assertEqual(type(frame), libpython.PyFrameObjectPtr)
|
||||
@@ -0,0 +1 @@
|
||||
# empty file
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
###############################################
|
||||
#
|
||||
# Odds and ends for debugging
|
||||
#
|
||||
###############################################
|
||||
|
||||
def print_call_chain(*args):
|
||||
import sys
|
||||
print(" ".join(map(str, args)))
|
||||
f = sys._getframe(1)
|
||||
while f:
|
||||
name = f.f_code.co_name
|
||||
s = f.f_locals.get('self', None)
|
||||
if s:
|
||||
c = getattr(s, "__class__", None)
|
||||
if c:
|
||||
name = "%s.%s" % (c.__name__, name)
|
||||
print("Called from: %s %s" % (name, f.f_lineno))
|
||||
f = f.f_back
|
||||
print("-" * 70)
|
||||
@@ -0,0 +1,2 @@
|
||||
from Cython.Distutils.build_ext import build_ext
|
||||
from Cython.Distutils.extension import Extension
|
||||
@@ -0,0 +1,25 @@
|
||||
import sys
|
||||
|
||||
if 'setuptools' in sys.modules:
|
||||
try:
|
||||
from setuptools.command.build_ext import build_ext as _build_ext
|
||||
except ImportError:
|
||||
# We may be in the process of importing setuptools, which tries
|
||||
# to import this.
|
||||
from distutils.command.build_ext import build_ext as _build_ext
|
||||
else:
|
||||
from distutils.command.build_ext import build_ext as _build_ext
|
||||
|
||||
|
||||
class new_build_ext(_build_ext, object):
|
||||
def finalize_options(self):
|
||||
if self.distribution.ext_modules:
|
||||
nthreads = getattr(self, 'parallel', None) # -j option in Py3.5+
|
||||
nthreads = int(nthreads) if nthreads else None
|
||||
from Cython.Build.Dependencies import cythonize
|
||||
self.distribution.ext_modules[:] = cythonize(
|
||||
self.distribution.ext_modules, nthreads=nthreads, force=self.force)
|
||||
super(new_build_ext, self).finalize_options()
|
||||
|
||||
# This will become new_build_ext in the future.
|
||||
from .old_build_ext import old_build_ext as build_ext
|
||||
@@ -0,0 +1,128 @@
|
||||
"""Pyrex.Distutils.extension
|
||||
|
||||
Provides a modified Extension class, that understands how to describe
|
||||
Pyrex extension modules in setup scripts."""
|
||||
|
||||
__revision__ = "$Id:$"
|
||||
|
||||
import sys
|
||||
import distutils.extension as _Extension
|
||||
|
||||
try:
|
||||
import warnings
|
||||
except ImportError:
|
||||
warnings = None
|
||||
|
||||
|
||||
class Extension(_Extension.Extension):
|
||||
# When adding arguments to this constructor, be sure to update
|
||||
# user_options.extend in build_ext.py.
|
||||
def __init__(self, name, sources,
|
||||
include_dirs=None,
|
||||
define_macros=None,
|
||||
undef_macros=None,
|
||||
library_dirs=None,
|
||||
libraries=None,
|
||||
runtime_library_dirs=None,
|
||||
extra_objects=None,
|
||||
extra_compile_args=None,
|
||||
extra_link_args=None,
|
||||
export_symbols=None,
|
||||
#swig_opts=None,
|
||||
depends=None,
|
||||
language=None,
|
||||
cython_include_dirs=None,
|
||||
cython_directives=None,
|
||||
cython_create_listing=False,
|
||||
cython_line_directives=False,
|
||||
cython_cplus=False,
|
||||
cython_c_in_temp=False,
|
||||
cython_gen_pxi=False,
|
||||
cython_gdb=False,
|
||||
no_c_in_traceback=False,
|
||||
cython_compile_time_env=None,
|
||||
**kw):
|
||||
|
||||
# Translate pyrex_X to cython_X for backwards compatibility.
|
||||
had_pyrex_options = False
|
||||
for key in list(kw):
|
||||
if key.startswith('pyrex_'):
|
||||
had_pyrex_options = True
|
||||
kw['cython' + key[5:]] = kw.pop(key)
|
||||
if had_pyrex_options:
|
||||
Extension.__init__(
|
||||
self, name, sources,
|
||||
include_dirs=include_dirs,
|
||||
define_macros=define_macros,
|
||||
undef_macros=undef_macros,
|
||||
library_dirs=library_dirs,
|
||||
libraries=libraries,
|
||||
runtime_library_dirs=runtime_library_dirs,
|
||||
extra_objects=extra_objects,
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=extra_link_args,
|
||||
export_symbols=export_symbols,
|
||||
#swig_opts=swig_opts,
|
||||
depends=depends,
|
||||
language=language,
|
||||
no_c_in_traceback=no_c_in_traceback,
|
||||
**kw)
|
||||
return
|
||||
|
||||
_Extension.Extension.__init__(
|
||||
self, name, sources,
|
||||
include_dirs=include_dirs,
|
||||
define_macros=define_macros,
|
||||
undef_macros=undef_macros,
|
||||
library_dirs=library_dirs,
|
||||
libraries=libraries,
|
||||
runtime_library_dirs=runtime_library_dirs,
|
||||
extra_objects=extra_objects,
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=extra_link_args,
|
||||
export_symbols=export_symbols,
|
||||
#swig_opts=swig_opts,
|
||||
depends=depends,
|
||||
language=language,
|
||||
**kw)
|
||||
|
||||
self.cython_include_dirs = cython_include_dirs or []
|
||||
self.cython_directives = cython_directives or {}
|
||||
self.cython_create_listing = cython_create_listing
|
||||
self.cython_line_directives = cython_line_directives
|
||||
self.cython_cplus = cython_cplus
|
||||
self.cython_c_in_temp = cython_c_in_temp
|
||||
self.cython_gen_pxi = cython_gen_pxi
|
||||
self.cython_gdb = cython_gdb
|
||||
self.no_c_in_traceback = no_c_in_traceback
|
||||
self.cython_compile_time_env = cython_compile_time_env
|
||||
|
||||
# class Extension
|
||||
|
||||
read_setup_file = _Extension.read_setup_file
|
||||
|
||||
|
||||
# reuse and extend original docstring from base class (if we can)
|
||||
if sys.version_info[0] < 3 and _Extension.Extension.__doc__:
|
||||
# -OO discards docstrings
|
||||
Extension.__doc__ = _Extension.Extension.__doc__ + """\
|
||||
cython_include_dirs : [string]
|
||||
list of directories to search for Pyrex header files (.pxd) (in
|
||||
Unix form for portability)
|
||||
cython_directives : {string:value}
|
||||
dict of compiler directives
|
||||
cython_create_listing_file : boolean
|
||||
write pyrex error messages to a listing (.lis) file.
|
||||
cython_line_directives : boolean
|
||||
emit pyx line numbers for debugging/profiling
|
||||
cython_cplus : boolean
|
||||
use the C++ compiler for compiling and linking.
|
||||
cython_c_in_temp : boolean
|
||||
put generated C files in temp directory.
|
||||
cython_gen_pxi : boolean
|
||||
generate .pxi file for public declarations
|
||||
cython_gdb : boolean
|
||||
generate Cython debug information for this extension for cygdb
|
||||
no_c_in_traceback : boolean
|
||||
emit the c file and line number from the traceback for exceptions
|
||||
"""
|
||||
@@ -0,0 +1,355 @@
|
||||
"""Cython.Distutils.old_build_ext
|
||||
|
||||
Implements a version of the Distutils 'build_ext' command, for
|
||||
building Cython extension modules.
|
||||
|
||||
Note that this module is deprecated. Use cythonize() instead.
|
||||
"""
|
||||
|
||||
__revision__ = "$Id:$"
|
||||
|
||||
import inspect
|
||||
import sys
|
||||
import os
|
||||
from distutils.errors import DistutilsPlatformError
|
||||
from distutils.dep_util import newer, newer_group
|
||||
from distutils import log
|
||||
from distutils.command import build_ext as _build_ext
|
||||
from distutils import sysconfig
|
||||
import warnings
|
||||
|
||||
|
||||
try:
|
||||
from __builtin__ import basestring
|
||||
except ImportError:
|
||||
basestring = str
|
||||
|
||||
|
||||
def _check_stack(path):
|
||||
try:
|
||||
for frame in inspect.getouterframes(inspect.currentframe(), 0):
|
||||
if path in frame[1].replace(os.sep, '/'):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
if (not _check_stack('setuptools/extensions.py')
|
||||
and not _check_stack('pyximport/pyxbuild.py')
|
||||
and not _check_stack('Cython/Distutils/build_ext.py')):
|
||||
warnings.warn(
|
||||
"Cython.Distutils.old_build_ext does not properly handle dependencies "
|
||||
"and is deprecated.")
|
||||
|
||||
|
||||
extension_name_re = _build_ext.extension_name_re
|
||||
|
||||
show_compilers = _build_ext.show_compilers
|
||||
|
||||
class Optimization(object):
|
||||
def __init__(self):
|
||||
self.flags = (
|
||||
'OPT',
|
||||
'CFLAGS',
|
||||
'CPPFLAGS',
|
||||
'EXTRA_CFLAGS',
|
||||
'BASECFLAGS',
|
||||
'PY_CFLAGS',
|
||||
)
|
||||
self.state = sysconfig.get_config_vars(*self.flags)
|
||||
self.config_vars = sysconfig.get_config_vars()
|
||||
|
||||
|
||||
def disable_optimization(self):
|
||||
"disable optimization for the C or C++ compiler"
|
||||
badoptions = ('-O1', '-O2', '-O3')
|
||||
|
||||
for flag, option in zip(self.flags, self.state):
|
||||
if option is not None:
|
||||
L = [opt for opt in option.split() if opt not in badoptions]
|
||||
self.config_vars[flag] = ' '.join(L)
|
||||
|
||||
def restore_state(self):
|
||||
"restore the original state"
|
||||
for flag, option in zip(self.flags, self.state):
|
||||
if option is not None:
|
||||
self.config_vars[flag] = option
|
||||
|
||||
|
||||
optimization = Optimization()
|
||||
|
||||
|
||||
class old_build_ext(_build_ext.build_ext):
|
||||
|
||||
description = "build C/C++ and Cython extensions (compile/link to build directory)"
|
||||
|
||||
sep_by = _build_ext.build_ext.sep_by
|
||||
user_options = _build_ext.build_ext.user_options[:]
|
||||
boolean_options = _build_ext.build_ext.boolean_options[:]
|
||||
help_options = _build_ext.build_ext.help_options[:]
|
||||
|
||||
# Add the pyrex specific data.
|
||||
user_options.extend([
|
||||
('cython-cplus', None,
|
||||
"generate C++ source files"),
|
||||
('cython-create-listing', None,
|
||||
"write errors to a listing file"),
|
||||
('cython-line-directives', None,
|
||||
"emit source line directives"),
|
||||
('cython-include-dirs=', None,
|
||||
"path to the Cython include files" + sep_by),
|
||||
('cython-c-in-temp', None,
|
||||
"put generated C files in temp directory"),
|
||||
('cython-gen-pxi', None,
|
||||
"generate .pxi file for public declarations"),
|
||||
('cython-directives=', None,
|
||||
"compiler directive overrides"),
|
||||
('cython-gdb', None,
|
||||
"generate debug information for cygdb"),
|
||||
('cython-compile-time-env', None,
|
||||
"cython compile time environment"),
|
||||
|
||||
# For backwards compatibility.
|
||||
('pyrex-cplus', None,
|
||||
"generate C++ source files"),
|
||||
('pyrex-create-listing', None,
|
||||
"write errors to a listing file"),
|
||||
('pyrex-line-directives', None,
|
||||
"emit source line directives"),
|
||||
('pyrex-include-dirs=', None,
|
||||
"path to the Cython include files" + sep_by),
|
||||
('pyrex-c-in-temp', None,
|
||||
"put generated C files in temp directory"),
|
||||
('pyrex-gen-pxi', None,
|
||||
"generate .pxi file for public declarations"),
|
||||
('pyrex-directives=', None,
|
||||
"compiler directive overrides"),
|
||||
('pyrex-gdb', None,
|
||||
"generate debug information for cygdb"),
|
||||
])
|
||||
|
||||
boolean_options.extend([
|
||||
'cython-cplus', 'cython-create-listing', 'cython-line-directives',
|
||||
'cython-c-in-temp', 'cython-gdb',
|
||||
|
||||
# For backwards compatibility.
|
||||
'pyrex-cplus', 'pyrex-create-listing', 'pyrex-line-directives',
|
||||
'pyrex-c-in-temp', 'pyrex-gdb',
|
||||
])
|
||||
|
||||
def initialize_options(self):
|
||||
_build_ext.build_ext.initialize_options(self)
|
||||
self.cython_cplus = 0
|
||||
self.cython_create_listing = 0
|
||||
self.cython_line_directives = 0
|
||||
self.cython_include_dirs = None
|
||||
self.cython_directives = None
|
||||
self.cython_c_in_temp = 0
|
||||
self.cython_gen_pxi = 0
|
||||
self.cython_gdb = False
|
||||
self.no_c_in_traceback = 0
|
||||
self.cython_compile_time_env = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name[:6] == 'pyrex_':
|
||||
return getattr(self, 'cython_' + name[6:])
|
||||
else:
|
||||
return _build_ext.build_ext.__getattr__(self, name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name[:6] == 'pyrex_':
|
||||
return setattr(self, 'cython_' + name[6:], value)
|
||||
else:
|
||||
# _build_ext.build_ext.__setattr__(self, name, value)
|
||||
self.__dict__[name] = value
|
||||
|
||||
def finalize_options (self):
|
||||
_build_ext.build_ext.finalize_options(self)
|
||||
if self.cython_include_dirs is None:
|
||||
self.cython_include_dirs = []
|
||||
elif isinstance(self.cython_include_dirs, basestring):
|
||||
self.cython_include_dirs = \
|
||||
self.cython_include_dirs.split(os.pathsep)
|
||||
if self.cython_directives is None:
|
||||
self.cython_directives = {}
|
||||
# finalize_options ()
|
||||
|
||||
def run(self):
|
||||
# We have one shot at this before build_ext initializes the compiler.
|
||||
# If --pyrex-gdb is in effect as a command line option or as option
|
||||
# of any Extension module, disable optimization for the C or C++
|
||||
# compiler.
|
||||
if self.cython_gdb or [1 for ext in self.extensions
|
||||
if getattr(ext, 'cython_gdb', False)]:
|
||||
optimization.disable_optimization()
|
||||
|
||||
_build_ext.build_ext.run(self)
|
||||
|
||||
def build_extensions(self):
|
||||
# First, sanity-check the 'extensions' list
|
||||
self.check_extensions_list(self.extensions)
|
||||
|
||||
for ext in self.extensions:
|
||||
ext.sources = self.cython_sources(ext.sources, ext)
|
||||
# Call original build_extensions
|
||||
_build_ext.build_ext.build_extensions(self)
|
||||
|
||||
def cython_sources(self, sources, extension):
|
||||
"""
|
||||
Walk the list of source files in 'sources', looking for Cython
|
||||
source files (.pyx and .py). Run Cython on all that are
|
||||
found, and return a modified 'sources' list with Cython source
|
||||
files replaced by the generated C (or C++) files.
|
||||
"""
|
||||
try:
|
||||
from Cython.Compiler.Main \
|
||||
import CompilationOptions, \
|
||||
default_options as cython_default_options, \
|
||||
compile as cython_compile
|
||||
from Cython.Compiler.Errors import PyrexError
|
||||
except ImportError:
|
||||
e = sys.exc_info()[1]
|
||||
print("failed to import Cython: %s" % e)
|
||||
raise DistutilsPlatformError("Cython does not appear to be installed")
|
||||
|
||||
new_sources = []
|
||||
cython_sources = []
|
||||
cython_targets = {}
|
||||
|
||||
# Setup create_list and cplus from the extension options if
|
||||
# Cython.Distutils.extension.Extension is used, otherwise just
|
||||
# use what was parsed from the command-line or the configuration file.
|
||||
# cplus will also be set to true is extension.language is equal to
|
||||
# 'C++' or 'c++'.
|
||||
#try:
|
||||
# create_listing = self.cython_create_listing or \
|
||||
# extension.cython_create_listing
|
||||
# cplus = self.cython_cplus or \
|
||||
# extension.cython_cplus or \
|
||||
# (extension.language != None and \
|
||||
# extension.language.lower() == 'c++')
|
||||
#except AttributeError:
|
||||
# create_listing = self.cython_create_listing
|
||||
# cplus = self.cython_cplus or \
|
||||
# (extension.language != None and \
|
||||
# extension.language.lower() == 'c++')
|
||||
|
||||
create_listing = self.cython_create_listing or \
|
||||
getattr(extension, 'cython_create_listing', 0)
|
||||
line_directives = self.cython_line_directives or \
|
||||
getattr(extension, 'cython_line_directives', 0)
|
||||
no_c_in_traceback = self.no_c_in_traceback or \
|
||||
getattr(extension, 'no_c_in_traceback', 0)
|
||||
cplus = self.cython_cplus or getattr(extension, 'cython_cplus', 0) or \
|
||||
(extension.language and extension.language.lower() == 'c++')
|
||||
cython_gen_pxi = self.cython_gen_pxi or getattr(extension, 'cython_gen_pxi', 0)
|
||||
cython_gdb = self.cython_gdb or getattr(extension, 'cython_gdb', False)
|
||||
cython_compile_time_env = self.cython_compile_time_env or \
|
||||
getattr(extension, 'cython_compile_time_env', None)
|
||||
|
||||
# Set up the include_path for the Cython compiler:
|
||||
# 1. Start with the command line option.
|
||||
# 2. Add in any (unique) paths from the extension
|
||||
# cython_include_dirs (if Cython.Distutils.extension is used).
|
||||
# 3. Add in any (unique) paths from the extension include_dirs
|
||||
includes = self.cython_include_dirs
|
||||
try:
|
||||
for i in extension.cython_include_dirs:
|
||||
if not i in includes:
|
||||
includes.append(i)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# In case extension.include_dirs is a generator, evaluate it and keep
|
||||
# result
|
||||
extension.include_dirs = list(extension.include_dirs)
|
||||
for i in extension.include_dirs:
|
||||
if not i in includes:
|
||||
includes.append(i)
|
||||
|
||||
# Set up Cython compiler directives:
|
||||
# 1. Start with the command line option.
|
||||
# 2. Add in any (unique) entries from the extension
|
||||
# cython_directives (if Cython.Distutils.extension is used).
|
||||
directives = self.cython_directives
|
||||
if hasattr(extension, "cython_directives"):
|
||||
directives.update(extension.cython_directives)
|
||||
|
||||
# Set the target_ext to '.c'. Cython will change this to '.cpp' if
|
||||
# needed.
|
||||
if cplus:
|
||||
target_ext = '.cpp'
|
||||
else:
|
||||
target_ext = '.c'
|
||||
|
||||
# Decide whether to drop the generated C files into the temp dir
|
||||
# or the source tree.
|
||||
|
||||
if not self.inplace and (self.cython_c_in_temp
|
||||
or getattr(extension, 'cython_c_in_temp', 0)):
|
||||
target_dir = os.path.join(self.build_temp, "pyrex")
|
||||
for package_name in extension.name.split('.')[:-1]:
|
||||
target_dir = os.path.join(target_dir, package_name)
|
||||
else:
|
||||
target_dir = None
|
||||
|
||||
newest_dependency = None
|
||||
for source in sources:
|
||||
(base, ext) = os.path.splitext(os.path.basename(source))
|
||||
if ext == ".py":
|
||||
# FIXME: we might want to special case this some more
|
||||
ext = '.pyx'
|
||||
if ext == ".pyx": # Cython source file
|
||||
output_dir = target_dir or os.path.dirname(source)
|
||||
new_sources.append(os.path.join(output_dir, base + target_ext))
|
||||
cython_sources.append(source)
|
||||
cython_targets[source] = new_sources[-1]
|
||||
elif ext == '.pxi' or ext == '.pxd':
|
||||
if newest_dependency is None \
|
||||
or newer(source, newest_dependency):
|
||||
newest_dependency = source
|
||||
else:
|
||||
new_sources.append(source)
|
||||
|
||||
if not cython_sources:
|
||||
return new_sources
|
||||
|
||||
module_name = extension.name
|
||||
|
||||
for source in cython_sources:
|
||||
target = cython_targets[source]
|
||||
depends = [source] + list(extension.depends or ())
|
||||
if(source[-4:].lower()==".pyx" and os.path.isfile(source[:-3]+"pxd")):
|
||||
depends += [source[:-3]+"pxd"]
|
||||
rebuild = self.force or newer_group(depends, target, 'newer')
|
||||
if not rebuild and newest_dependency is not None:
|
||||
rebuild = newer(newest_dependency, target)
|
||||
if rebuild:
|
||||
log.info("cythoning %s to %s", source, target)
|
||||
self.mkpath(os.path.dirname(target))
|
||||
if self.inplace:
|
||||
output_dir = os.curdir
|
||||
else:
|
||||
output_dir = self.build_lib
|
||||
options = CompilationOptions(cython_default_options,
|
||||
use_listing_file = create_listing,
|
||||
include_path = includes,
|
||||
compiler_directives = directives,
|
||||
output_file = target,
|
||||
cplus = cplus,
|
||||
emit_linenums = line_directives,
|
||||
c_line_in_traceback = not no_c_in_traceback,
|
||||
generate_pxi = cython_gen_pxi,
|
||||
output_dir = output_dir,
|
||||
gdb_debug = cython_gdb,
|
||||
compile_time_env = cython_compile_time_env)
|
||||
result = cython_compile(source, options=options,
|
||||
full_module_name=module_name)
|
||||
else:
|
||||
log.info("skipping '%s' Cython extension (up-to-date)", target)
|
||||
|
||||
return new_sources
|
||||
|
||||
# cython_sources ()
|
||||
|
||||
# class build_ext
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.bool cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.buffer cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.bytes cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.cobject cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.complex cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.dict cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.exc cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.float cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.function cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.getargs cimport *
|
||||
@@ -0,0 +1,2 @@
|
||||
# Present for backwards compatibility
|
||||
from cpython.instance cimport *
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user