拡張の型の定義: 雑多なトピック

この節ではさまざまな実装可能なタイプメソッドと、それらが何をするものであるかについて、ざっと説明します。

以下は PyTypeObject の定義です。 デバッグビルド でしか使われないいくつかのメンバは省いてあります:

typedef struct _typeobject {
    PyObject_VAR_HEAD
    const char *tp_name; /* 表示用。形式は "<module>.<name>" */
    Py_ssize_t tp_basicsize, tp_itemsize; /* 割り当て用 */

    /* 標準の操作を実装するメソッド */

    destructor tp_dealloc;
    Py_ssize_t tp_vectorcall_offset;
    getattrfunc tp_getattr;
    setattrfunc tp_setattr;
    PyAsyncMethods *tp_as_async; /* 以前の名前は tp_compare (Python 2)
                                    または tp_reserved (Python 3) */
    reprfunc tp_repr;

    /* 標準クラス用のメソッド群 */

    PyNumberMethods *tp_as_number;
    PySequenceMethods *tp_as_sequence;
    PyMappingMethods *tp_as_mapping;

    /* その他の標準の操作 (バイナリ互換性のためこの位置に配置) */

    hashfunc tp_hash;
    ternaryfunc tp_call;
    reprfunc tp_str;
    getattrofunc tp_getattro;
    setattrofunc tp_setattro;

    /* オブジェクトを入出力バッファとして扱う関数 */
    PyBufferProcs *tp_as_buffer;

    /* 任意機能や拡張機能の有無を定義するフラグ */
    unsigned long tp_flags;

    const char *tp_doc; /* ドキュメント文字列 */

    /* リリース 2.0 で意味を割り当て */
    /* アクセス可能な全オブジェクトについて関数を呼び出す */
    traverseproc tp_traverse;

    /* 保持するオブジェクトへの参照を削除する */
    inquiry tp_clear;

    /* リリース 2.1 で意味を割り当て */
    /* 拡張比較 */
    richcmpfunc tp_richcompare;

    /* 弱参照を有効にする */
    Py_ssize_t tp_weaklistoffset;

    /* イテレータ */
    getiterfunc tp_iter;
    iternextfunc tp_iternext;

    /* 属性デスクリプタとサブクラス化に関するもの */
    PyMethodDef *tp_methods;
    PyMemberDef *tp_members;
    PyGetSetDef *tp_getset;
    // ヒープ型では強参照、静的な型では借用参照
    PyTypeObject *tp_base;
    PyObject *tp_dict;
    descrgetfunc tp_descr_get;
    descrsetfunc tp_descr_set;
    Py_ssize_t tp_dictoffset;
    initproc tp_init;
    allocfunc tp_alloc;
    newfunc tp_new;
    freefunc tp_free; /* 低レベルのメモリ解放ルーチン */
    inquiry tp_is_gc; /* PyObject_IS_GC 用 */
    PyObject *tp_bases;
    PyObject *tp_mro; /* メソッド解決順序 */
    PyObject *tp_cache; /* 現在は未使用 */
    void *tp_subclasses;  /* 静的な組み込み型ではインデックス */
    PyObject *tp_weaklist; /* 静的な組み込み型では未使用 */
    destructor tp_del;

    /* 型の属性キャッシュのバージョンタグ。バージョン 2.6 で追加。
     * ゼロならキャッシュは無効であり、初期化が必要。
     */
    unsigned int tp_version_tag;

    destructor tp_finalize;
    vectorcallfunc tp_vectorcall;

    /* この型に関心を持つ型ウォッチャーを表すビット集合 */
    unsigned char tp_watched;

    /* 使用された tp_version_tag 値の数。
     * 属性キャッシュがこの型で無効な場合は、
     * _Py_ATTR_CACHE_UNUSED に設定する (独自の MRO エントリーなどによる)。
     * それ以外の場合は MAX_VERSIONS_PER_CLASS (別の場所で定義) 以下。
     */
    uint16_t tp_versions_used;
} PyTypeObject;

たくさんの メソッドがありますね。でもそんなに心配する必要はありません。定義したい型があるなら、実装するのはこのうちのごくわずかですむことがほとんどです。

すでに予想されているでしょうが、この構造体について入念に見ていき、様々なハンドラについてより詳しい情報を提供します。 しかしこれらのメンバが構造体中で定義されている順番は無視します。というのは、これらのメンバの現れる順序は歴史的な遺産によるものだからです。 多くの場合いちばん簡単なのは、必要とするメンバがすべて含まれている例をとってきて、新しく作る型に合わせて値を変更することです。

const char *tp_name; /* 表示用 */

これは型の名前です。前の章で説明したように、これは色々な場面で現れ、ほとんどは診断目的で使われるものです。 それなので、そのような場面で役に立つであろう名前を選んでください!

Py_ssize_t tp_basicsize, tp_itemsize; /* 割り当て用 */

これらのフィールドは、この型のオブジェクトが新しく作成されるときにどれだけのメモリを割り当てればよいのかをランタイムに指示します。Python には可変長の構造体 (文字列やタプルなどを想像してください) に対する組み込みのサポートがある程度あり、ここで tp_itemsize メンバが使われます。これらについてはあとでふれます。

const char *tp_doc;

ここには、Python スクリプトが obj.__doc__ を参照してドキュメント文字列を取得するときに返す文字列 (またはそのアドレス) を設定します。

では次に、型の基本的なメソッドに進みます。ほとんどの拡張の型がこのメソッドを実装します。

ファイナライズとメモリ解放

destructor tp_dealloc;

型のインスタンスの参照カウントがゼロになり、Python インタプリタがそれを潰して再利用したくなると、この関数が呼ばれます。解放すべきメモリをその型が保持していたり、それ以外にも実行すべき後処理がある場合は、それらをここに入れられます。オブジェクトそれ自体もここで解放される必要があります。この関数の例は、以下のようなものです:

static void
newdatatype_dealloc(PyObject *op)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    free(self->obj_UnderlyingDatatypePtr);
    Py_TYPE(self)->tp_free(self);
}

型がガベージコレクションをサポートする場合、デストラクタでは、メンバフィールドをクリアする前に PyObject_GC_UnTrack() を呼び出す必要があります。

static void
newdatatype_dealloc(PyObject *op)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    PyObject_GC_UnTrack(op);
    Py_CLEAR(self->other_obj);
    ...
    Py_TYPE(self)->tp_free(self);
}

メモリ解放関数でひとつ重要なのは、処理待ちの例外にいっさい手をつけないことです。なぜなら、解放用の関数は Python インタプリタがスタックを元の状態に戻すときに呼ばれることが多いからです。そして (通常の関数からの復帰でなく) 例外のためにスタックが巻き戻されるときは、すでに発生している例外からメモリ解放関数を守るものはありません。解放用の関数がおこなう動作が追加の Python のコードを実行してしまうと、それらは例外が発生していることを検知するかもしれません。これはインタプリタが誤解させるエラーを発生させることにつながります。これを防ぐ正しい方法は、安全でない操作を実行する前に処理待ちの例外を保存しておき、終わったらそれを元に戻すことです。これは PyErr_Fetch() および PyErr_Restore() 関数を使うことによって可能になります:

static void
my_dealloc(PyObject *obj)
{
    MyObject *self = (MyObject *) obj;
    PyObject *cbresult;

    if (self->my_callback != NULL) {
        PyObject *err_type, *err_value, *err_traceback;

        /* 現在の例外の状態を保存する */
        PyErr_Fetch(&err_type, &err_value, &err_traceback);

        cbresult = PyObject_CallNoArgs(self->my_callback);
        if (cbresult == NULL) {
           PyErr_WriteUnraisable(self->my_callback);
        }
        else {
            Py_DECREF(cbresult);
        }

        /* 保存した例外の状態を復元する */
        PyErr_Restore(err_type, err_value, err_traceback);

        Py_DECREF(self->my_callback);
    }
    Py_TYPE(self)->tp_free(self);
}

注釈

メモリ解放関数で安全に行えることには制限があります。 まず、型が (tp_traverse、tp_clear の一方または両方を使って) ガベージコレクションをサポートする場合、tp_dealloc が呼ばれる時点で、オブジェクトの一部のメンバがすでにクリアされているか、ファイナライズされている可能性があります。 また、tp_dealloc の中ではオブジェクトの参照カウントがゼロになっており、不安定な状態にあります。 上の例のような単純でないオブジェクトや API の呼び出しは、tp_dealloc を再び呼び出し、二重解放によるクラッシュを引き起こす可能性があります。

Python 3.4 からは、複雑なファイナライズのコードは tp_dealloc に置かず、代わりに新しく導入された tp_finalize という型メソッドを使うことが推奨されています。

参考

PEP 442 で新しいファイナライズの仕組みが説明されています。

オブジェクト表現

Python では、オブジェクトの文字列表現を生成するのに 2つのやり方があります: repr() 関数を使う方法と、 str() 関数を使う方法です。 (print() 関数は単に str() を呼び出します。) これらのハンドラはどちらも省略できます。

reprfunc tp_repr;
reprfunc tp_str;

tp_repr ハンドラは呼び出されたインスタンスの文字列表現を格納した文字列オブジェクトを返す必要があります。簡単な例は以下のようなものです:

static PyObject *
newdatatype_repr(PyObject *op)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    return PyUnicode_FromFormat("Repr-ified_newdatatype{{size:%d}}",
                                self->obj_UnderlyingDatatypePtr->size);
}

tp_repr ハンドラを指定しない場合、インタープリタはその型の tp_name とオブジェクトを一意に識別する値を使った表現を生成します。

tp_str ハンドラと str() の関係は、上の tp_repr ハンドラと repr() の関係に相当します。つまり、これは Python のコードがオブジェクトのインスタンスに対して str() を呼び出したときに呼ばれます。この関数の実装は tp_repr ハンドラのそれと非常に似ていますが、得られる文字列表現は人間が読むことを意図されています。 tp_str が指定されていない場合、かわりに tp_repr ハンドラが使われます。

以下は簡単な例です:

static PyObject *
newdatatype_str(PyObject *op)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    return PyUnicode_FromFormat("Stringified_newdatatype{{size:%d}}",
                                self->obj_UnderlyingDatatypePtr->size);
}

属性を管理する

属性を持つオブジェクトでは、対応する型が属性の解決方法を制御する関数を提供する必要があります。 属性を定義する場合は取得用の関数が、属性の設定を許可する場合は設定用の関数が必要です。 属性の削除は特別な場合で、ハンドラには新しい値として NULL を渡します。

Python は属性ハンドラの組を二つサポートしています。 属性をサポートする型は、どちらか一組の関数だけを実装すれば十分です。 違いは、一方が属性名を char* で受け取り、もう一方は PyObject* で受け取る点です。 各型は実装に都合のよい方を使えます。

getattrfunc  tp_getattr;        /* char * 版 */
setattrfunc  tp_setattr;
/* ... */
getattrofunc tp_getattro;       /* PyObject * 版 */
setattrofunc tp_setattro;

オブジェクトの属性へのアクセスが常に単純な操作で済む場合 (この後で説明します)、PyObject* 版の属性管理関数として使える汎用の実装があります。 Python 2.2 以降、型固有の属性ハンドラが実際に必要になることはほとんどなくなりました。 ただし、新しい汎用の仕組みを使うように更新されていない例も数多くあります。

総称的な属性を管理する

ほとんどの型は 単純な 属性を使うだけです。では、どのような属性が単純だといえるのでしょうか? それが満たすべき条件はごくわずかです:

  1. PyType_Ready() が呼ばれたとき、すでに属性の名前がわかっていること。

  2. 属性が取得または設定されたことを記録するための特別な処理も、属性の値に応じた処理も不要であること。

これらの条件は、属性の値、値を計算するタイミング、関連するデータの格納方法には何の制約も課しません。

PyType_Ready() は、型オブジェクトが参照する三つのテーブルを使って デスクリプタ を作成し、型オブジェクトの辞書に格納します。 各デスクリプタは、インスタンスオブジェクトの一つの属性へのアクセスを制御します。 テーブルはいずれも省略可能です。 三つとも NULL の場合、その型のインスタンスが持つ属性は基底型から継承したものだけになります。 この場合、属性の処理を基底型に任せられるように、tp_getattro と tp_setattro フィールドも NULL にしてください。

テーブルはタイプオブジェクト中の 3つのメンバとして宣言されています:

struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;

tp_methods が NULL でない場合は、PyMethodDef 構造体の配列を指す必要があります。 テーブル内の各エントリーは、次の構造体のインスタンスです。

typedef struct PyMethodDef {
    const char  *ml_name;       /* メソッド名 */
    PyCFunction  ml_meth;       /* 実装関数 */
    int          ml_flags;      /* フラグ */
    const char  *ml_doc;        /* ドキュメント文字列 */
} PyMethodDef;

型が提供するメソッドごとに一つのエントリーを定義します。 基底型から継承するメソッドのエントリーは不要です。 末尾には、配列の終わりを示す番兵となるエントリーをもう一つ追加する必要があります。 番兵の ml_name フィールドは NULL にしなければなりません。

2番目のテーブルは、インスタンス中に格納されるデータと直接対応づけられた属性を定義するのに使います。いくつもの C の原始的な型がサポートされており、アクセスを読み出し専用にも読み書き可能にもできます。このテーブルで使われる構造体は次のように定義されています:

typedef struct PyMemberDef {
    const char *name;
    int         type;
    Py_ssize_t  offset;
    int         flags;
    const char *doc;
} PyMemberDef;

テーブルの各エントリーから、インスタンス構造体の値を取り出せる デスクリプタ が作成され、型に追加されます。 type フィールドには Py_T_INT や Py_T_DOUBLE のような型コードを設定します。 この値は、Python の値と C の値を相互に変換する方法を決めるために使います。 flags フィールドには、属性へのアクセス方法を制御するフラグを格納します。 Py_READONLY を設定すると、Python コードからの値の設定を禁止できます。

tp_members テーブルから実行時に使うデスクリプタを作成する方法には、興味深い利点があります。 この方法で定義した属性は、テーブルにテキストを指定するだけでドキュメント文字列を関連付けられます。 アプリケーションはイントロスペクション API を使ってクラスオブジェクトからデスクリプタを取得し、その __doc__ 属性からドキュメント文字列を取得できます。

As with the tp_methods table, a sentinel entry with a ml_name value of NULL is required.

特定の型に特化した属性の管理

簡単にするため、ここでは char* 版だけを示します。 インターフェースの char* 版と PyObject* 版の違いは、名前を渡す引数の型だけです。 この例は、上の汎用の例と実質的に同じ処理を行いますが、Python 2.2 で追加された汎用の仕組みは使いません。 ハンドラ関数の呼び出し方を説明するので、機能を拡張する必要がある場合に、何をすべきかが分かるでしょう。

The tp_getattr handler is called when the object requires an attribute look-up. It is called in the same situations where the __getattr__() method of a class would be called.

以下に例を示します。:

static PyObject *
newdatatype_getattr(PyObject *op, char *name)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    if (strcmp(name, "data") == 0) {
        return PyLong_FromLong(self->data);
    }

    PyErr_Format(PyExc_AttributeError,
                 "'%.100s' object has no attribute '%.400s'",
                 Py_TYPE(self)->tp_name, name);
    return NULL;
}

tp_setattr ハンドラは、クラスのインスタンスの __setattr__() または __delattr__() メソッドが呼ばれる状況で呼び出されます。 属性を削除する場合、第 3 引数は NULL になります。 次は単に例外を送出する例です。 本当にこれだけを行いたいのであれば、tp_setattr ハンドラを NULL に設定してください。

static int
newdatatype_setattr(PyObject *op, char *name, PyObject *v)
{
    PyErr_Format(PyExc_RuntimeError, "Read-only attribute: %s", name);
    return -1;
}

オブジェクトの比較

richcmpfunc tp_richcompare;

tp_richcompare ハンドラは、比較が必要なときに呼び出されます。 __lt__() などの 拡張比較メソッド に相当し、PyObject_RichCompare() と PyObject_RichCompareBool() からも呼び出されます。

この関数は、二つの Python オブジェクトと演算子を引数として受け取ります。 演算子は Py_EQ、Py_NE、Py_LE、Py_GE、Py_LT、Py_GT のいずれかです。 指定された演算子で二つのオブジェクトを比較し、比較が成功した場合は Py_True または Py_False を返してください。 比較が実装されておらず、もう一方のオブジェクトの比較メソッドを試すべきことを示すには Py_NotImplemented を、例外を設定した場合は NULL を返してください。

これは内部ポインタのサイズが等しければ等しいと見なすデータ型のサンプル実装です:

static PyObject *
newdatatype_richcmp(PyObject *lhs, PyObject *rhs, int op)
{
    newdatatypeobject *obj1 = (newdatatypeobject *) lhs;
    newdatatypeobject *obj2 = (newdatatypeobject *) rhs;
    PyObject *result;
    int c, size1, size2;

    /* 両方の引数が newdatatype 型であることを
       確認するコードは省略 */

    size1 = obj1->obj_UnderlyingDatatypePtr->size;
    size2 = obj2->obj_UnderlyingDatatypePtr->size;

    switch (op) {
    case Py_LT: c = size1 <  size2; break;
    case Py_LE: c = size1 <= size2; break;
    case Py_EQ: c = size1 == size2; break;
    case Py_NE: c = size1 != size2; break;
    case Py_GT: c = size1 >  size2; break;
    case Py_GE: c = size1 >= size2; break;
    }
    result = c ? Py_True : Py_False;
    return Py_NewRef(result);
 }

抽象的なプロトコルのサポート

Python はいくつもの 抽象的な “プロトコル”をサポートしています。これらを使用する特定のインターフェイスについては 抽象オブジェクトレイヤ (Abstract Objects Layer) で解説されています。

A number of these abstract interfaces were defined early in the development of the Python implementation. In particular, the number, mapping, and sequence protocols have been part of Python since the beginning. Other protocols have been added over time. For protocols which depend on several handler routines from the type implementation, the older protocols have been defined as optional blocks of handlers referenced by the type object. For newer protocols there are additional slots in the main type object, with a flag bit being set to indicate that the slots are present and should be checked by the interpreter. (The flag bit does not indicate that the slot values are non-NULL. The flag may be set to indicate the presence of a slot, but a slot may still be unfilled.)

PyNumberMethods   *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods  *tp_as_mapping;

オブジェクトを数値、シーケンス、マッピングとして振る舞わせるには、それぞれ C の PyNumberMethods、PySequenceMethods、PyMappingMethods 型の構造体のアドレスを設定します。 これらの構造体に適切な値を設定するのは、実装する側の責任です。 それぞれの使用例は、Python の配布ソースの Objects ディレクトリで見つけられます。

hashfunc tp_hash;

この関数は、もし使うことにしたならば、データ型のインスタンスのハッシュ番号を返すようにします。次のは単純な例です:

static Py_hash_t
newdatatype_hash(PyObject *op)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    Py_hash_t result;
    result = self->some_size + 32767 * self->some_number;
    if (result == -1) {
        result = -2;
    }
    return result;
}

Py_hash_t は、幅がプラットフォームによって異なる符号付き整数型です。 tp_hash が -1 を返すとエラーを示すため、上の例のように、ハッシュの計算に成功したときはこの値を返さないよう注意してください。

ternaryfunc tp_call;

この関数は、その型のインスタンスが「関数として呼び出される」ときに呼ばれます。たとえばもし obj1 にそのインスタンスが入っていて、Python スクリプトで obj1('hello') を実行したとすると、 tp_call ハンドラが呼ばれます。

この関数は 3つの引数をとります:

  1. self は呼び出しの対象となるデータ型のインスタンスです。 たとえば呼び出しが obj1('hello') の場合、self は obj1 になります。

  2. args は呼び出しの引数を格納しているタプルです。ここから引数を取り出すには PyArg_ParseTuple() を使います。

  3. kwds は、渡されたキーワード引数の辞書です。 これが NULL でなく、キーワード引数をサポートする場合は、PyArg_ParseTupleAndKeywords() で引数を取り出してください。 キーワード引数をサポートしない場合にこれが NULL でなければ、キーワード引数をサポートしていないことを示すメッセージとともに TypeError を送出してください。

以下は tp_call の簡易な実装です:

static PyObject *
newdatatype_call(PyObject *op, PyObject *args, PyObject *kwds)
{
    newdatatypeobject *self = (newdatatypeobject *) op;
    PyObject *result;
    const char *arg1;
    const char *arg2;
    const char *arg3;

    if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
        return NULL;
    }
    result = PyUnicode_FromFormat(
        "Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",
        self->obj_UnderlyingDatatypePtr->size,
        arg1, arg2, arg3);
    return result;
}
/* イテレータ */
getiterfunc tp_iter;
iternextfunc tp_iternext;

これらの関数はイテレータプロトコルをサポートします。 どちらのハンドラも、呼び出し対象のインスタンスを唯一の引数として受け取り、新しい参照を返します。 エラーの場合は例外を設定し、NULL を返してください。 tp_iter は Python の __iter__() メソッドに、tp_iternext は Python の __next__() メソッドに対応します。

Any iterable object must implement the tp_iter handler, which must return an iterator object. Here the same guidelines apply as for Python classes:

  • 独立した複数のイテレータをサポートできるコレクション (リストやタプルなど) では、tp_iter を呼び出すたびに新しいイテレータを作成して返す必要があります。

  • 一度しか反復できないオブジェクト (ファイルオブジェクトのように、通常は反復に副作用があるもの) は、自身への新しい参照を返すことで tp_iter を実装できます。 その場合は tp_iternext ハンドラも実装する必要があります。

イテレータ オブジェクトは、tp_iter と tp_iternext の両方を実装する必要があります。 イテレータの tp_iter ハンドラは、そのイテレータへの新しい参照を返します。 tp_iternext ハンドラは、反復する次のオブジェクトがあれば、それへの新しい参照を返します。 反復の終わりに達した場合、tp_iternext は例外を設定せずに NULL を返しても、NULL を返す とともに StopIteration を設定しても構いません。 例外を設定しない方が、パフォーマンスが少しよくなることがあります。 実際にエラーが発生した場合は、tp_iternext は必ず例外を設定し、NULL を返してください。

弱参照(Weak Reference)のサポート

Python の弱参照の実装では、数値のようにパフォーマンスが重要なオブジェクトにオーバーヘッドを課さず、どの型でも弱参照の仕組みに参加できるようにすることを目指しています。

参考

weakref モジュールのドキュメント。

オブジェクトが弱参照をサポートするには、拡張の型が tp_flags フィールドの Py_TPFLAGS_MANAGED_WEAKREF ビットを設定する必要があります。 従来の tp_weaklistoffset フィールドはゼロのままにしてください。

このフラグを設定する場合は、Py_TPFLAGS_HAVE_GC も設定するべきです。

具体的には、静的に宣言した型オブジェクトは次のようになります。

static PyTypeObject TrivialType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    /* ... 簡略化のためほかのメンバは省略 ... */
    .tp_flags = Py_TPFLAGS_MANAGED_WEAKREF | ...,
};

このほかに必要なのは、tp_dealloc で (PyObject_ClearWeakRefs() を呼び出して) 弱参照をすべてクリアすることだけです。

static void
Trivial_dealloc(PyObject *op)
{
    /* デストラクタを呼ぶ前に、まず弱参照をクリアする */
    PyObject_ClearWeakRefs(op);
    /* ... 簡略化のため残りの破棄処理は省略 ... */
    Py_TYPE(op)->tp_free(op);
}

その他いろいろ

新しいデータ型の特定のメソッドを実装する方法を知るには、CPython のソースコードを入手してください。 Objects ディレクトリで、tp_ に目的の関数名を続けた文字列 (tp_richcompare など) を C ソースファイルから検索すると、実装したい関数の例が見つかります。

オブジェクトが実装中の型のインスタンスであることを確認するには、PyObject_TypeCheck() 関数を使ってください。 例えば、次のように使います。

if (!PyObject_TypeCheck(some_object, &MyType)) {
    PyErr_SetString(PyExc_TypeError, "arg #1 not a mything");
    return NULL;
}

参考

CPython のソースリリースをダウンロードできます。

https://www.python.org/downloads/source/

CPython のソースコードが開発されている GitHub 上の CPython プロジェクトです。

https://github.com/python/cpython