Cythonization エラーには、少なくとも 4 つの解決策があります (これらの結果は にありますcython == 0.29.24
)。
ファイルを追加し、ビルド中の s の名前を、ビルド中のモジュールのサブモジュール、つまりexample_package/__init__.pxd
and に変更します(質問では、これらはandになります)。この変更は、以下で説明するように、インストールされたサブモジュールと
をインポートするために必要です。以下で説明するように、キーワード parameter と argument がないため、この場合、インストールされたパッケージは実際には名前空間パッケージであることに注意してください。Extension
example_package.other
example_package.driver
Test.other
Test.driver
driver
other
packages=['example_package']
example_package/__init__.py
ファイルを追加し、ビルド中の の名前を、Extension
ビルド中のモジュールのサブモジュール、つまりexample_package.other
とに変更しexample_package.driver
ます。この場合でも、__init__.py
が存在する場合、インストールされるパッケージexample_package
は名前空間パッケージになります。通常のパッケージにするpackages=['example_package']
には、関数に渡す必要がありますsetuptools.setup
。
の追加と同様に__init__.pxd
、この変更は、インストールされたサブモジュールをインポートするために必要です。
example_package/__init__.pxd
ファイルを追加し、ステートメントをファイル内のcimport
絶対に変更します(パッケージはこの代替方法でビルドおよびインストールされますが、s の名前も変更する必要があるため、インポートしません):cimport
example_package/driver.pyx
Extension
from . import other
from example_package cimport other
example_package/__init__.py
前の項目で行ったように、ファイルを追加し、ステートメントをfile 内のcimport
絶対に変更します。パッケージはこれでビルドおよびインストールされますが、インポートされません。cimport
example_package/driver.pyx
質問は明示的に相対インポートを求めているため、その意味で、最初の2つの選択肢は相対インポートで機能するため、質問に対する答えです。
上記の 4 つの変更のいずれかにより、次のエラーが回避されます。
Error compiling Cython file:
------------------------------------------------------------
...
from . import other
from . cimport other
^
------------------------------------------------------------
example_package/driver.pyx:2:0: relative cimport beyond main package is not allowed
しかし、すでに上で述べたように、また以下で説明するように、Extension
インストールされたサブモジュールをインポートするには、最初または 2 番目の選択肢の名前を変更する必要があります (さらに、4 番目の選択肢でパラメーターとキーワード引数を渡すとpackages=[PACKAGE_NAME]
、Python パッケージexample_package
をインポートできますが、そのサブモジュールdriver
とother
)。
修正済みsetup.py
私が推奨するファイルsetup.py
は、すべての追加変更 (上記のビルドとインストールに必要な変更だけではありません) は次のとおりです。
"""Installation script."""
import os
import setuptools
try:
from Cython.Build import cythonize
cy_ext = f'{os.extsep}pyx'
except ImportError:
# this case is intended for use when installing from
# a source distribution (produced with `sdist`),
# which, as recommended by Cython documentation,
# should include the generated `*.c` files,
# in order to enable installation in absence of `cython`
print('`import cython` failed')
cy_ext = f'{os.extsep}c'
PACKAGE_NAME = 'example_package'
def run_setup():
"""Build and install package."""
ext_modules = extensions()
setuptools.setup(
name=PACKAGE_NAME,
ext_modules=ext_modules,
packages=[PACKAGE_NAME],
package_dir={PACKAGE_NAME: PACKAGE_NAME})
def extensions():
"""Return C extensions, cythonize as needed."""
extensions = dict(
other=setuptools.extension.Extension(
f'{PACKAGE_NAME}.other',
sources=[f'{PACKAGE_NAME}/other{cy_ext}'],),
driver=setuptools.extension.Extension(
f'{PACKAGE_NAME}.driver',
sources=[f'{PACKAGE_NAME}/driver{cy_ext}'],))
if cy_ext == f'{os.extsep}pyx':
ext_modules = list()
for k, v in extensions.items():
c = cythonize(
[v],
# show_all_warnings=True # this line requires `cython >= 3.0`
)
ext_modules.append(c[0])
else:
ext_modules = list(extensions.values())
return ext_modules
if __name__ == '__main__':
run_setup()
その他の変更
この回答の他の変更は、パッケージを正常にビルドおよびインストールするために必要ではありませんが、他の理由で推奨されます。他のいくつかの変更については、以下に動機を説明します。
ご了承ください:
example_package/__init__.pxd
または追加するだけexample_package/__init__.py
では不十分であり、
Extension
名前を変えるだけでは不十分で、
cimport
ステートメントをに変更するだけでfrom example_package cimport other
は不十分です。
ビルドとインストールには、これらの変更のうち 2 つが一緒に必要です。つまり、前述の 4 つの選択肢の 1 つです。
Cython のソースとから構築された拡張モジュールをインポートできるようにするには、拡張の名前を次のように変更する必要もあります。driver.pyx
other.pyx
Extension('example_package.other', ...)
Extension('example_package.driver', ...)
が名前空間パッケージになったimport
ため、これが機能することに注意してください(CPython用語集のエントリ)。example_package
>>>
<module 'example_package' (namespace)>
>>> import example_package.driver
>>> import example_package.other
(また、使用したファイルのパラメーターinclude_dirs
を省略しました。これを以下に含めます。)setuptools.setup
setup.py
これらの変更は、パッケージのビルドとインストール、および拡張モジュールのインポートに必要です。拡張モジュールが含まれていない (したがって、名前空間パッケージになっていない) 場合に、インストールされたパッケージを Python からインポートするには:
- ディレクトリにファイル
__init__.py
を追加する必要がありますexample_package/
(問題のディレクトリはディレクトリですTest/
)。
- キーワード引数
packages=[
example_package],
を関数に渡す必要がありますsetuptools.setup
。
それ以外の場合、ステートメントimport example_package
はModuleNotFoundError
. ファイルの追加は、パッケージを通常のパッケージ(CPython用語集のエントリ__init__.py
) にするためにも必要です。これは通常、名前空間パッケージの代わりに意図されているものです。
を使用するかどうか__init__.pxd
通常の Python パッケージには__init__.py
ファイルが含まれています。__init__.pxd
ファイルは、他のパッケージがヘッダーを必要とする場合にのみ関連します*.pxd
。そうでない場合、上記の 4 つの解決策は基本的に 2 つの解決策であり、それぞれにまたはの代替手段example_package/__init__.py
があるため、ファイルで十分であるように思われます。__init__.py
__init__.pxd
したがって、ファイルとその配置に関する私の推奨事項は次のとおりです。
.
├── example_package
│ ├── __init__.py
│ ├── driver.pyx
│ ├── other.pxd
│ └── other.pyx
└── setup.py
両方の変更が必要です
ファイルを追加するだけ__init__.pxd
で、cythonization エラーが発生します。
Error compiling Cython file:
------------------------------------------------------------
...
from . import other
from . cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: relative cimport beyond main package is not allowed
cimport
ステートメントのみを変更すると ( なしで__init__.pxd
)、cythonization エラーが発生します。
Error compiling Cython file:
------------------------------------------------------------
...
#!/usr/bin/env python
from . import other
from example_package cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: 'example_package.pxd' not found
Error compiling Cython file:
------------------------------------------------------------
...
#!/usr/bin/env python
from . import other
from example_package cimport other
^
------------------------------------------------------------
example_package/driver.pyx:3:0: 'example_package/other.pxd' not found
パッケージの命名
example_package
上記ではパッケージの名前として書いていますTest/
が、これが実際に機能することを確認し、必要な最小限の変更が__init__.pxd
ファイルとfrom example_package cimport other
.
均一性のために、このパッケージ名でTest/
実行するときにディレクトリの名前も実際に変更しましたが、現時点では大文字と小文字を区別するファイルシステムを使用していないため、キーワード引数と一緒に名前が付けられsetup.py
たディレクトリであるかどうかはわかりません。問題では、大文字と小文字を区別するファイルシステムで問題が発生した可能性があります。test/
name='Test',
setup.py
そう:
Test
パッケージ名とディレクトリ名として使用Test
すると、ビルドとインストールに役立ちました。
test
パッケージ名とtest
ディレクトリ名として使用すると、ビルドとインストールに役立ちました。
別のパッケージ名を使用することをお勧めします。また、以下の理由により:
- パッケージに名前が付けられている場合のインポートは
Test
、ステートメントで行われimport Test
ます。書き込みは別のimport test
パッケージをインポートします (以下を参照)。
- パッケージ名として使用すると、ファイルが追加された場合でも、以下で説明する理由により
test
、インストールされたパッケージがインポートされません。test
__init__.py
いずれにせよ、以下に説明する理由により、メイン パッケージのテスト ハーネスとしてのみ使用することを意図した補助パッケージであっても、パッケージ名を変更することをお勧めします。
また、小文字のパッケージ命名はPEP 8によって義務付けられているためtest
、これはテストのディレクトリであると理解される可能性がありますが、これが実際にメイン パッケージの例であることが意図されている場合はそうではありません。
パッケージとディレクトリに名前を付けたときに、ビルドおよびインストール後に発生するエラーtest
は次のとおりです (ドット ... は、実際の出力を編集した結果です)。
>>> import test
>>> test
<module 'test' from '.../lib/python3.9/test/__init__.py'>
つまり、CPython には次のようなパッケージが含まれていますtest
。
このtest
パッケージには、Python のすべてのリグレッション テストと、モジュールtest.support
およびtest.regrtest
.
したがって、この名前test
は、インストール後にインポートすることを意図したサンプル パッケージには使用できません (ただし、パッケージはビルドされてインストールされ、さらには によってアンインストールされpip uninstall -y test
ます)。
別の詳細は、from test cimport other
コンパイルされたとしても実際には間違っているということです。ビルドされたtest
パッケージが実際に魔法のようにインポートされた場合 (CPython のtest
パッケージが存在する場合)、実行時にこのcimport
ステートメントは CPython のtest
パッケージにデフォルト設定されていたからです。それにもかかわらず、Cython の翻訳は、これをビルドされたパッケージcimport
から実際にインポートされた他の形式に変換する可能性があります。CPython のパッケージが存在する場合test.other
、インストールされたパッケージのインポートは不可能であるように見えるため、これが実行時エラーを引き起こしたかどうかは不明です。test
test
cimport
また、次の点に注意してください。
注:このtest
パッケージは、Python による内部使用のみを目的としています。これは、Python の中心的な開発者のために文書化されています。ここに記載されているコードは、Python のリリース間で予告なく変更または削除される可能性があるため、Python の標準ライブラリ以外でこのパッケージを使用することはお勧めできません。
すべての実験の合間に、 を実行しrm -rf build dist *.egg-info test/*.c
ます。そのため、ファイル配置を以前に示したものに変更する前に、私が使用したものは質問と同じです。
パッケージの名前をexample_package
質問のファイル内のパラメーターに指定された引数に基づいて、実際にインストールするパッケージが含まれているexample_package
と仮定して、パッケージの名前を に変更しました。test/
name=
setup.py
この名前変更の動機は、「test」または「tests」が通常、Python パッケージに付随するテストのディレクトリに名前を付けるために使用されることです。このようなディレクトリや、テストの使用方法については、さまざまな取り決めがあります。次のセクションでは、テストを配置するための私の提案について説明します。
可能性に関しては、次のセクションで説明する以外の配置が一般的に使用されており、パッケージ自体内のディレクトリにテストを配置することも含まれます。myProject/
質問が書いてあり、ファイルがあることを考えると、質問がmyProject/__init__.py
実際にそのような取り決めを使用しているかどうかはわかりません。
ただし、その場合は、実際driver
にother
はテスト モジュールになります。Test
モジュールが行うことである別のパッケージ(質問で呼ばれる)としてテストをインストールしますが、メインパッケージのモジュールでmyProject/setup.py
あることを示唆してdriver
いるother
ため、メインパッケージは「テスト」と呼ばれます。
そうでない場合、つまりdriver
とother
が実際にテスト モジュールであり、メイン パッケージのセットアップ スクリプトでsetup.py
はなく、メイン パッケージのテストのみを目的とした「補助」パッケージをビルドおよびインストールするセットアップ スクリプトである場合 (この場合は「myProject」という名前で、質問setup.py
のディレクトリを含むディレクトリに存在する場合)、への名前を変更すると、これがメインパッケージであることに対応しません。(Cython コードを含むテスト ハーネス パッケージを用意することも興味深いため、コンパイルが必要であり、場合によってはインストールも必要です。)myProject/
Test
example_package/
その場合、おそらく のTest
代わりに名前を変更できますtests_of_example_package
。つまり、その場合、パッケージの名前に「test」という単語を含めることは適切ですが、パッケージを補助として修飾することexample_package
は明示的であるように見えます。明示的は暗黙的よりも優れています ( PEP 20 )。
(テストは__init__.py
、これを補助 Python パッケージとしてインストールしない場合でも (を使用して) パッケージとして配置されることがあります (付属するメイン Python パッケージのテスト ハーネスとしてのみ意図されています)。動機は、テスト スイートの共通モジュールをインポートできるようにすることです。複数のテスト モジュールで使用されますが、それ自体はテスト ランナーによって直接実行されるモジュールではありません。)
これがメインのパッケージなら、サンプルでは例を書く目的で「Test」を使用したと思います。もしそうなら、名前を変更する唯一の理由 (小文字は別として) は、メイン パッケージ自体とそのテストを区別することです。
PEP 8では、Python パッケージの小文字の名前が義務付けられています。
アンダースコアの使用はお勧めできませんが、Python パッケージの名前もすべて小文字で短くする必要があります。
のアンダースコアexample_package
は単なる例です。
テストの手配
テストtest/
は、Python パッケージを含むディレクトリと同じディレクトリにあり、パッケージにちなんで名付けられたディレクトリに配置される場合があります。たとえば、このアプローチを強くお勧めします (このツリーはプログラムで作成されましたtree
)。
.
├── example_package
│ └── __init__.py
├── setup.py
└── tests
└── module_name_test.py
パッケージをソース ディレクトリから誤ってインポートせずにテストするには、パッケージがインストールexample_package
されている場所 (通常site-packages/
は. これは、テストに対する最も信頼性の高いアプローチであり、各テスト フレームワークの動作、テスト フレームワークのさまざまな構成オプションの動作、オプション同士の相互作用、テスト フレームワーク自体のバグがテストに与える影響に依存しません。cd
tests/
このようにして、example_package
他のディレクトリ配置を使用する理由がなくても、パッケージ ソースをディレクトリ内に配置できます。
Python モジュールのシバン
ファイル内のシバン*.pyx
は効果がないため、削除できます。シバン行は Cython によって Python コメント行として扱われ、Cython*.c
がファイルから生成するファイル内のどこかに後で C コメント内に移動され*.pyx
ます。したがって、効果はありません。gcc
Cython が行うように(Cython が を呼び出すかgcc
、または別のコンパイラがシステム、環境パス、環境変数に依存するかどうかにかかわらず)、直接呼び出し (または別の C コンパイラ) によってコンパイルされることを意図した C ソースでのシバン行の使用を認識していません。 、およびその他の情報)。
また、シバンは、実行可能ファイルとして実行される可能性のある Python モジュールにのみ関連します。これは、Python パッケージ内のモジュールに当てはまることを意図したものではないため、そこでシバン行が使用されることはほとんどありません。
例外は、実験やデバッグの目的など、開発中に直接実行されることがまれなパッケージ モジュールである可能性があります。__main__
それにもかかわらず、そのようなモジュールにはスタンザがあると予想されます。
そのため、シバンが関連する Python モジュールには、通常、__main__
スタンザもあります。
完全を期すために、setup.py
は として実行されることを意図し__main__
ており、__main__
スタンザがありますが、セットアップ スクリプトを実行する方法 (使用しない場合はpip
--usingpip
を強くお勧めします) は によるものpython setup.py
であるため、 にシバンは必要ありませsetup.py
ん (シバンは表示されません)。質問の中にあります-完全を期すためにこれについて言及しています)。
distutils
モジュール]() は、PEP 632で指定されているように Python 3.10 で非推奨になり、Python 3.12 で削除されます。
Cython が拡張機能に存在しない場合の切り替え.c
、代わりに.pyx
これは、 Cython の推奨事項と一致しています。
生成されたファイルと Cython ソースを配布することを強くお勧めします.c
。これにより、ユーザーは Cython を使用可能にする必要なくモジュールをインストールできます。
setup.py
変更されないままのモジュールスコープ変数の大文字の名前(「定数」)
モジュールスコープの Python 変数は、定数として使用することを意図しています。つまり、最初の代入後も変更されません。PEP 8では、アンダースコア付きの大文字の識別子を持つことが義務付けられています。
通常、定数はモジュール レベルで定義され、すべて大文字で書かれ、アンダースコアで単語が区切られます。例には、 および が含まMAX_OVERFLOW
れTOTAL
ます。
したがって、識別子PACKAGE_NAME
.
Python >= 3.6 を必要とするフォーマット済み文字列リテラルを使用しました。
モジュール内の関数としてコードを配置するsetup.py
これは、関数名を使用してコードのさまざまなセクションに名前を付けることができ、スタンザ__main__
を含めることにより、として実行された場合にのみコードを実行できるため、外部コードに関連する可能性のある特定の機能をインポートして使用できるため、一般的には良い方法ですフレームワーク) 必ずしもすべてのコードを実行する必要はありません。たとえば、関数を実行する必要はありません。__main__
setup.py
setuptools.setup
質問は最小限の実例を示しているため、質問には小さなsetup.py
ものが関連しています。このセクションは、質問ではなく、実際のパッケージで何をすべきかの推奨事項として書いています。
内のモジュールと関数のドキュメント文字列にも同じことが当てはまりますsetup.py
。
また、関数をトップダウンで配置することをお勧めします。このレイアウトは読みやすいため、呼び出し元が呼び出し先の上に配置されます。
一般性のために使用os.extsep
しましたが、ドットを使用しても機能すると思いますが、読みやすくなっています。
パッケージの配置
前述のように、ビルド エラー「メイン パッケージを超える相対 cimport は許可されていません」を回避するために必要な質問の例への唯一の変更は、または のいずれかの追加__init__.py
、__init__.pxd
および の絶対cimport
内部driver.pyx
または名前の変更のいずれかでしたExtensions
。
ファイルの削除__init__.py
__init__.py
最終版では、 と同じディレクトリにあるファイルを削除しましたsetup.py
。私の理解では、このファイルはこの例では効果がありません。test/
この例がメイン パッケージのディレクトリとして使用することを意図している場合は、すべて__init__.py
が 内に表示されtest/
ます。
test/
が実際にはメイン パッケージのテストの補助パッケージである場合、 はメイン パッケージ__init__.py
の一部であり、パッケージとは無関係test/
です。ただし、その場合、メイン パッケージとテスト ハーネス パッケージの両方のビルドを担当するsetup.py
上記のファイルが存在するようです。myProject/
絶対インポートの使用
language_level
cython < 3.0.0
Python 3 でも、デフォルトの inは 2です。
language_level
(2/3/3str)
モジュールのコンパイルに使用する Python 言語レベルをグローバルに設定します。デフォルトは Python 2 との互換性です。Python 3 ソース コード セマンティクスを有効にするには、モジュールの開始時にこれを 3 (または 3str) に設定するか、"-3" または "--3str" コマンド ライン オプションをコンパイラに渡します。
質問は Python 3.5 と を使用してcython == 0.23.4
いるため、これが当てはまります。
では、デフォルトの Cython セマンティクスが変更されていcython >= 3.0.0
ます。
デフォルトの言語レベルは3str
、Python 3 セマンティクスなどに変更されました。
Python 2 と Python 3 の両方のセマンティクス (プレリリースを渡すcompiler_directives=dict(language_level=3)
、またはプレリリースをインストールするcython == 3.0.0a8
) を使用すると、最初の 2 つのソリューション (相対インポートを使用する) が機能します。
それにもかかわらず、PEP 8 では絶対インポートが推奨されています。
インポート システムが正しく構成されていない場合、通常はより読みやすく、より適切に動作する (または少なくともより適切なエラー メッセージを表示する) 傾向があるため、絶対インポートをお勧めします ...
絶対インポートは、パッケージの構造のリファクタリングに対しても堅牢です。それらは明示的であり、明示的は暗黙的よりも優れています ( PEP 20 )。
driver.pyx
この変更後のモジュールは次のようになります。
from example_package import other
from example_package cimport other
この回答のコードは、Python パッケージsetup.py
のファイルに記述した内容に基づいています。download.py
dd