[Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
Hi,

I suspect that this will be put into a proper PEP at some point, but I'd
like to bring this up for discussion first. This came out of issues 13429
and 16392.

http://bugs.python.org/issue13429

http://bugs.python.org/issue16392

Stefan


The problem
===

Python modules and extension modules are not being set up in the same way.
For Python modules, the module is created and set up first, then the module
code is being executed. For extensions, i.e. shared libraries, the module
init function is executed straight away and does both the creation and
initialisation. This means that it knows neither the __file__ it is being
loaded from nor its package (i.e. its FQMN). This hinders relative imports
and resource loading. In Py3, it's also not being added to sys.modules,
which means that a (potentially transitive) re-import of the module will
really try to reimport it and thus run into an infinite loop when it
executes the module init function again. And without the FQMN, it's not
trivial to correctly add the module to sys.modules either.

We specifically run into this for Cython generated modules, for which it's
not uncommon that the module init code has the same level of complexity as
that of any 'regular' Python module. Also, the lack of a FQMN and correct
file path hinders the compilation of __init__.py modules, i.e. packages,
especially when relative imports are being used at module init time.

The proposal


I propose to split the extension module initialisation into two steps in
Python 3.4, in a backwards compatible way.

Step 1: The current module init function can be reduced to just creating
the module instance and returning it (and potentially doing some simple C
level setup). Optionally, after creating the module (and this is the new
part), the module init code can register a C callback function that will be
called after setting up the module.

Step 2: The shared library importer receives the module instance from the
module init function, adds __file__, __path__, __package__ and friends to
the module dict, and then checks for the callback. If non-NULL, it calls it
to continue the module initialisation by user code.

The callback


The callback is defined as follows::

int (*PyModule_init_callback)(PyObject* the_module,
  PyModuleInitContext* context)

"PyModuleInitContext" is a struct that is meant mostly for making the
callback more future proof by allowing additional parameters to be passed
in. For now, I can see a use case for the following fields::

struct PyModuleInitContext {
char* module_name;
char* qualified_module_name;
}

Both names are encoded in UTF-8. As for the file path, I consider it best
to retrieve it from the module's __file__ attribute as a Python string
object to reduce filename encoding problems.

Note that this struct argument is not strictly required, but given that
this proposal would have been much simpler if the module init function had
accepted such an argument in the first place, I consider it a good idea not
to let this chance pass by again.

The registration of the callback uses a new C-API function:

int PyModule_SetInitFunction(PyObject* module,
 PyModule_init_callback callback)

The function name uses "Set" instead of "Register" to make it clear that
there is only one such function per module.

An alternative would be a new module creation function "PyModule_Create3()"
that takes the callback as third argument, in addition to what
"PyModule_Create2()" accepts. This would require users to explicitly pass
in the (second) version argument, which might be considered only a minor issue.

Implementation
==

The implementation requires local changes to the extension module importer
and a new C-API function. In order to store the callback, it should use a
new field in the module object struct.

Open questions
==

It is not clear how extensions should be handled that register more than
one module in their module init function, e.g. compiled packages. One
possibility would be to leave the setup to the user, who would have to know
all FQMNs anyway in this case, although not the import file path.
Alternatively, the import machinery could use a stack to remember for which
modules a callback was registered during the last init function call, set
up all of them and then call their callbacks. It's not clear if this meets
the intention of the user.

Alternatives


1) It would be possible to make extension modules optionally export another
symbol, e.g. "PyInit2_modulename", that the shared library loader would
call in addition to the required function "PyInit_modulename". This would
remove the need for a new API that registers the above callback. The
drawback is that it also makes it easier to write broken code because a
Python version or implementation that does not support this second symbol
would simply not call it, with

Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread M.-A. Lemburg
On 08.11.2012 13:47, Stefan Behnel wrote:
> Hi,
> 
> I suspect that this will be put into a proper PEP at some point, but I'd
> like to bring this up for discussion first. This came out of issues 13429
> and 16392.
> 
> http://bugs.python.org/issue13429
> 
> http://bugs.python.org/issue16392
> 
> Stefan
> 
> 
> The problem
> ===
> 
> Python modules and extension modules are not being set up in the same way.
> For Python modules, the module is created and set up first, then the module
> code is being executed. For extensions, i.e. shared libraries, the module
> init function is executed straight away and does both the creation and
> initialisation. This means that it knows neither the __file__ it is being
> loaded from nor its package (i.e. its FQMN). This hinders relative imports
> and resource loading. In Py3, it's also not being added to sys.modules,
> which means that a (potentially transitive) re-import of the module will
> really try to reimport it and thus run into an infinite loop when it
> executes the module init function again. And without the FQMN, it's not
> trivial to correctly add the module to sys.modules either.
> 
> We specifically run into this for Cython generated modules, for which it's
> not uncommon that the module init code has the same level of complexity as
> that of any 'regular' Python module. Also, the lack of a FQMN and correct
> file path hinders the compilation of __init__.py modules, i.e. packages,
> especially when relative imports are being used at module init time.
> 
> The proposal
> 
> 
> ... [callbacks] ...
>
> Alternatives
> 
> ...
> 3) The callback could be registered statically in the PyModuleDef struct by
> adding a new field. This is not trivial to do in a backwards compatible way
> because the struct would grow longer without explicit initialisation by
> existing user code. Extending PyModuleDef_HEAD_INIT might be possible but
> would still break at least binary compatibility.

I think the above is the cleaner approach than the callback mechanism.
There's no problem in adding new slots to the end of the PyModuleDef struct
- we've been doing that for years in many other structs :-)

All you have to do is bump the Python API version number.

(Martin's PEP http://www.python.org/dev/peps/pep-3121/ has the details)

-- 
Marc-Andre Lemburg
eGenix.com

Professional Python Services directly from the Source  (#1, Nov 08 2012)
>>> Python Projects, Consulting and Support ...   http://www.egenix.com/
>>> mxODBC.Zope/Plone.Database.Adapter ...   http://zope.egenix.com/
>>> mxODBC, mxDateTime, mxTextTools ...http://python.egenix.com/


::: Try our new mxODBC.Connect Python Database Interface for free ! 

   eGenix.com Software, Skills and Services GmbH  Pastor-Loeh-Str.48
D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
   Registered at Amtsgericht Duesseldorf: HRB 46611
   http://www.egenix.com/company/contact/
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
M.-A. Lemburg, 08.11.2012 14:01:
> On 08.11.2012 13:47, Stefan Behnel wrote:
>> I suspect that this will be put into a proper PEP at some point, but I'd
>> like to bring this up for discussion first. This came out of issues 13429
>> and 16392.
>>
>> http://bugs.python.org/issue13429
>>
>> http://bugs.python.org/issue16392
>>
>> Stefan
>>
>>
>> The problem
>> ===
>>
>> Python modules and extension modules are not being set up in the same way.
>> For Python modules, the module is created and set up first, then the module
>> code is being executed. For extensions, i.e. shared libraries, the module
>> init function is executed straight away and does both the creation and
>> initialisation. This means that it knows neither the __file__ it is being
>> loaded from nor its package (i.e. its FQMN). This hinders relative imports
>> and resource loading. In Py3, it's also not being added to sys.modules,
>> which means that a (potentially transitive) re-import of the module will
>> really try to reimport it and thus run into an infinite loop when it
>> executes the module init function again. And without the FQMN, it's not
>> trivial to correctly add the module to sys.modules either.
>>
>> We specifically run into this for Cython generated modules, for which it's
>> not uncommon that the module init code has the same level of complexity as
>> that of any 'regular' Python module. Also, the lack of a FQMN and correct
>> file path hinders the compilation of __init__.py modules, i.e. packages,
>> especially when relative imports are being used at module init time.
>>
>> The proposal
>> 
>>
>> ... [callbacks] ...
>>
>> Alternatives
>> 
>> ...
>> 3) The callback could be registered statically in the PyModuleDef struct by
>> adding a new field. This is not trivial to do in a backwards compatible way
>> because the struct would grow longer without explicit initialisation by
>> existing user code. Extending PyModuleDef_HEAD_INIT might be possible but
>> would still break at least binary compatibility.
> 
> I think the above is the cleaner approach than the callback mechanism.

Oh, definitely.


> There's no problem in adding new slots to the end of the PyModuleDef struct
> - we've been doing that for years in many other structs :-)
> 
> All you have to do is bump the Python API version number.
> 
> (Martin's PEP http://www.python.org/dev/peps/pep-3121/ has the details)

The difference is that this specific struct is provided by user code and
(typically) initialised statically. There is no guarantee that user code
that does not expect the additional field will initialise it to 0. Failing
that, I don't see how we could trust its value in any way.

Stefan


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
Stefan Behnel, 08.11.2012 14:20:
> M.-A. Lemburg, 08.11.2012 14:01:
>> On 08.11.2012 13:47, Stefan Behnel wrote:
>>> I suspect that this will be put into a proper PEP at some point, but I'd
>>> like to bring this up for discussion first. This came out of issues 13429
>>> and 16392.
>>>
>>> http://bugs.python.org/issue13429
>>>
>>> http://bugs.python.org/issue16392
>>>
>>> Stefan
>>>
>>>
>>> The problem
>>> ===
>>>
>>> Python modules and extension modules are not being set up in the same way.
>>> For Python modules, the module is created and set up first, then the module
>>> code is being executed. For extensions, i.e. shared libraries, the module
>>> init function is executed straight away and does both the creation and
>>> initialisation. This means that it knows neither the __file__ it is being
>>> loaded from nor its package (i.e. its FQMN). This hinders relative imports
>>> and resource loading. In Py3, it's also not being added to sys.modules,
>>> which means that a (potentially transitive) re-import of the module will
>>> really try to reimport it and thus run into an infinite loop when it
>>> executes the module init function again. And without the FQMN, it's not
>>> trivial to correctly add the module to sys.modules either.
>>>
>>> We specifically run into this for Cython generated modules, for which it's
>>> not uncommon that the module init code has the same level of complexity as
>>> that of any 'regular' Python module. Also, the lack of a FQMN and correct
>>> file path hinders the compilation of __init__.py modules, i.e. packages,
>>> especially when relative imports are being used at module init time.
>>>
>>> The proposal
>>> 
>>>
>>> ... [callbacks] ...
>>>
>>> Alternatives
>>> 
>>> ...
>>> 3) The callback could be registered statically in the PyModuleDef struct by
>>> adding a new field. This is not trivial to do in a backwards compatible way
>>> because the struct would grow longer without explicit initialisation by
>>> existing user code. Extending PyModuleDef_HEAD_INIT might be possible but
>>> would still break at least binary compatibility.
>>
>> I think the above is the cleaner approach than the callback mechanism.
> 
> Oh, definitely.
> 
> 
>> There's no problem in adding new slots to the end of the PyModuleDef struct
>> - we've been doing that for years in many other structs :-)
>>
>> All you have to do is bump the Python API version number.
>>
>> (Martin's PEP http://www.python.org/dev/peps/pep-3121/ has the details)
> 
> The difference is that this specific struct is provided by user code and
> (typically) initialised statically. There is no guarantee that user code
> that does not expect the additional field will initialise it to 0. Failing
> that, I don't see how we could trust its value in any way.

Hmm - you're actually right. In C, uninitialised fields in a static struct
are set to 0 automatically. Same case as the type structs. That makes your
objection perfectly valid. I'll rewrite and shorten the proposal.

Thanks!

Stefan


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] Update - Re: Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
Hi,

here's an updated proposal, adopting Marc-Andre's improvement that uses a
new field in the PyModuleDef struct to register the callback. Note that
this change no longer keeps up binary compatibility, which may or may not
be acceptable for Python 3.4.

Stefan


The problem
===

Python modules and extension modules are not being set up in the same way.
For Python modules, the module is created and set up first, then the module
code is being executed. For extensions, i.e. shared libraries, the module
init function is executed straight away and does both the creation and
initialisation. This means that it knows neither the __file__ it is being
loaded from nor its package (i.e. its FQMN). This hinders relative imports
and resource loading. In Py3, it's also not being added to sys.modules,
which means that a (potentially transitive) re-import of the module will
really try to reimport it and thus run into an infinite loop when it
executes the module init function again. And without the FQMN, it's not
trivial to correctly add the module to sys.modules either.

We specifically run into this for Cython generated modules, for which it's
not uncommon that the module init code has the same level of complexity as
that of any 'regular' Python module. Also, the lack of a FQMN and correct
file path hinders the compilation of __init__.py modules, i.e. packages,
especially when relative imports are being used at module init time.

The proposal


I propose to split the extension module initialisation into two steps in
Python 3.4, in a backwards compatible way.

Step 1: The current module init function can be reduced to just creating
the module instance and returning it (and potentially doing some simple C
level setup). Additionally, and this is the new part, the module init code
can register a C callback function in its PyModuleDef struct that will be
called after setting up the module.

Step 2: The shared library importer receives the module instance from the
module init function, adds __file__, __path__, __package__ and friends to
the module dict, and then checks for the callback. If non-NULL, it calls it
to continue the module initialisation by user code.

The callback


The callback is defined as follows::

int (*PyModule_init_callback)(PyObject* the_module,
  PyModuleInitContext* context)

"PyModuleInitContext" is a struct that is meant mostly for making the
callback more future proof by allowing additional parameters to be passed
in. For now, I can see a use case for the following fields::

struct PyModuleInitContext {
char* module_name;
char* qualified_module_name;
}

Both names are encoded in UTF-8. As for the file path, I consider it best
to retrieve it from the module's __file__ attribute as a Python string
object to reduce filename encoding problems.

Note that this struct argument is not strictly required (it could be a
simple "inquiry" function), but given that this proposal would have been
much simpler if the module init function had accepted such an argument in
the first place, I consider it a good idea not to let this chance pass by
again. The counter arguments would be "keep it simple" and "we already pass
in the whole module (and its dict) anyway". Up for debate!

The registration of the callback uses a new field "m_init" in the
PyModuleDef struct::

typedef struct PyModuleDef{
  PyModuleDef_Base m_base;
  const char* m_name;
  const char* m_doc;
  Py_ssize_t m_size;
  PyMethodDef *m_methods;
  inquiry m_reload;
  traverseproc m_traverse;
  inquiry m_clear;
  freefunc m_free;  /* --- original fields up to here */
  PyModule_init_callback m_init;   /* post-setup init callback */
} PyModuleDef;

Implementation
==

The implementation requires local changes to the extension module importer
and a new field in the PyModuleDef struct.

Open questions
==

It is not clear how extensions should be handled that register more than
one module in their module init function, e.g. compiled packages. One
possibility would be to leave the setup to the user, who would have to know
all FQMNs anyway in this case, although not the import file path.
Alternatively, the import machinery could use a stack to remember for which
modules a callback was registered during the last init function call, set
up all of them and then call their callbacks. It's not clear if this meets
the intention of the user. It's not guaranteed that all of these modules
will be related to the module that registered them, in the sense that they
should receive the same setup. The best way to fix this correctly might be
to make users pass the setup explicitly into the module creation functions
in Python 4 (see alternatives below), so that the setup and sys.modules
registration can happen directly at this point.

Alternatives


1) It would be possible to make extension modules o

Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Brett Cannon
On Thu, Nov 8, 2012 at 7:47 AM, Stefan Behnel  wrote:

> Hi,
>
> I suspect that this will be put into a proper PEP at some point, but I'd
> like to bring this up for discussion first. This came out of issues 13429
> and 16392.
>
> http://bugs.python.org/issue13429
>
> http://bugs.python.org/issue16392
>
> Stefan
>
>
> The problem
> ===
>
> Python modules and extension modules are not being set up in the same way.
> For Python modules, the module is created and set up first, then the module
> code is being executed. For extensions, i.e. shared libraries, the module
> init function is executed straight away and does both the creation and
> initialisation. This means that it knows neither the __file__ it is being
> loaded from nor its package (i.e. its FQMN). This hinders relative imports
> and resource loading. In Py3, it's also not being added to sys.modules,
> which means that a (potentially transitive) re-import of the module will
> really try to reimport it and thus run into an infinite loop when it
> executes the module init function again. And without the FQMN, it's not
> trivial to correctly add the module to sys.modules either.
>
> We specifically run into this for Cython generated modules, for which it's
> not uncommon that the module init code has the same level of complexity as
> that of any 'regular' Python module. Also, the lack of a FQMN and correct
> file path hinders the compilation of __init__.py modules, i.e. packages,
> especially when relative imports are being used at module init time.
>

Or to put it another way, importlib doesn't give you a nice class to
inherit from which will handle all of the little details of creating a
blank module (or fetching from sys.modules if you are reloading), setting
__file__, __cached__, __package__, __name__, __loader__, and (optionally)
__path__ for you, and then cleaning up if something goes wrong. It's a pain
to do all of this yourself and to get all the details right (i.e. there's a
reason that @importlib.util.module_for_loader exists).


>
> The proposal
> 
>
> I propose to split the extension module initialisation into two steps in
> Python 3.4, in a backwards compatible way.
>
> Step 1: The current module init function can be reduced to just creating
> the module instance and returning it (and potentially doing some simple C
> level setup). Optionally, after creating the module (and this is the new
> part), the module init code can register a C callback function that will be
> called after setting up the module.
>

Why even bother with the module creation? Why can't Python do that as well
and then call the callback?


>
> Step 2: The shared library importer receives the module instance from the
> module init function, adds __file__, __path__, __package__ and friends to
> the module dict, and then checks for the callback. If non-NULL, it calls it
> to continue the module initialisation by user code.


> The callback
> 
>
> The callback is defined as follows::
>
> int (*PyModule_init_callback)(PyObject* the_module,
>   PyModuleInitContext* context)
>
> "PyModuleInitContext" is a struct that is meant mostly for making the
> callback more future proof by allowing additional parameters to be passed
> in. For now, I can see a use case for the following fields::
>
> struct PyModuleInitContext {
> char* module_name;
> char* qualified_module_name;
> }
>
> Both names are encoded in UTF-8. As for the file path, I consider it best
> to retrieve it from the module's __file__ attribute as a Python string
> object to reduce filename encoding problems.
>
> Note that this struct argument is not strictly required, but given that
> this proposal would have been much simpler if the module init function had
> accepted such an argument in the first place, I consider it a good idea not
> to let this chance pass by again.
>
> The registration of the callback uses a new C-API function:
>
> int PyModule_SetInitFunction(PyObject* module,
>  PyModule_init_callback callback)
>
> The function name uses "Set" instead of "Register" to make it clear that
> there is only one such function per module.
>
> An alternative would be a new module creation function "PyModule_Create3()"
> that takes the callback as third argument, in addition to what
> "PyModule_Create2()" accepts. This would require users to explicitly pass
> in the (second) version argument, which might be considered only a minor
> issue.
>
> Implementation
> ==
>
> The implementation requires local changes to the extension module importer
> and a new C-API function. In order to store the callback, it should use a
> new field in the module object struct.
>
> Open questions
> ==
>
> It is not clear how extensions should be handled that register more than
> one module in their module init function, e.g. compiled packages. One
> possibility would be to leave the setup to th

Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
Hi Brett,

thanks for the feedback.

Brett Cannon, 08.11.2012 15:41:
> On Thu, Nov 8, 2012 at 7:47 AM, Stefan Behnel wrote:
>> I propose to split the extension module initialisation into two steps in
>> Python 3.4, in a backwards compatible way.
>>
>> Step 1: The current module init function can be reduced to just creating
>> the module instance and returning it (and potentially doing some simple C
>> level setup). Optionally, after creating the module (and this is the new
>> part), the module init code can register a C callback function that will be
>> called after setting up the module.
> 
> Why even bother with the module creation? Why can't Python do that as well
> and then call the callback?
> 
> 
>> Step 2: The shared library importer receives the module instance from the
>> module init function, adds __file__, __path__, __package__ and friends to
>> the module dict, and then checks for the callback. If non-NULL, it calls it
>> to continue the module initialisation by user code.
> [...]
> An alternative to the alternative is that if the PyInit2 function exists
> it's called instead of the the PyInit function, and then the PyInit
> function is nothing more than a single line function call (or whatever the
> absolute bare minimum is) into some helper that calls the PyInit2 call
> properly for backwards ABI compatibility (i.e. passes in whatever details
> are lost by the indirection in function call). That provides an eventual
> upgrade path of dropping PyInit and moving over to PyInit2.

In that case, you'd have to export the PyModuleDef descriptor as well,
because that's what tells CPython how the module behaves and what to do
with it to set it up properly (e.g. allocate module state space on the heap).

In fact, if the module init function became a field in the descriptor, it
would be enough (taking backwards compatibility aside) if *only* the
descriptor was exported and used by the module loader.

With the caveat that this might kill some less common but not necessarily
illegitimate use cases that do more than just creating and initialising a
single module...

Stefan


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Brett Cannon
On Thu, Nov 8, 2012 at 10:00 AM, Stefan Behnel  wrote:

> Hi Brett,
>
> thanks for the feedback.
>
> Brett Cannon, 08.11.2012 15:41:
> > On Thu, Nov 8, 2012 at 7:47 AM, Stefan Behnel wrote:
> >> I propose to split the extension module initialisation into two steps in
> >> Python 3.4, in a backwards compatible way.
> >>
> >> Step 1: The current module init function can be reduced to just creating
> >> the module instance and returning it (and potentially doing some simple
> C
> >> level setup). Optionally, after creating the module (and this is the new
> >> part), the module init code can register a C callback function that
> will be
> >> called after setting up the module.
> >
> > Why even bother with the module creation? Why can't Python do that as
> well
> > and then call the callback?
> >
> >
> >> Step 2: The shared library importer receives the module instance from
> the
> >> module init function, adds __file__, __path__, __package__ and friends
> to
> >> the module dict, and then checks for the callback. If non-NULL, it
> calls it
> >> to continue the module initialisation by user code.
> > [...]
> > An alternative to the alternative is that if the PyInit2 function exists
> > it's called instead of the the PyInit function, and then the PyInit
> > function is nothing more than a single line function call (or whatever
> the
> > absolute bare minimum is) into some helper that calls the PyInit2 call
> > properly for backwards ABI compatibility (i.e. passes in whatever details
> > are lost by the indirection in function call). That provides an eventual
> > upgrade path of dropping PyInit and moving over to PyInit2.
>
> In that case, you'd have to export the PyModuleDef descriptor as well,
> because that's what tells CPython how the module behaves and what to do
> with it to set it up properly (e.g. allocate module state space on the
> heap).
>

True.


>
> In fact, if the module init function became a field in the descriptor, it
> would be enough (taking backwards compatibility aside) if *only* the
> descriptor was exported and used by the module loader.
>
>
Also true.


> With the caveat that this might kill some less common but not necessarily
> illegitimate use cases that do more than just creating and initialising a
> single module...
>

You mean creating another module in the init function? That's fine, but
that should be a call to __import__ anyway and that should handle things
properly. Else you are circumventing the import system and you can do
everything from scratch. I don't see why this would stop you from doing
anything you want, it just simplifies the common case.
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Stefan Behnel
Brett Cannon, 08.11.2012 16:06:
> On Thu, Nov 8, 2012 at 10:00 AM, Stefan Behnel  wrote:
> 
>> Hi Brett,
>>
>> thanks for the feedback.
>>
>> Brett Cannon, 08.11.2012 15:41:
>>> On Thu, Nov 8, 2012 at 7:47 AM, Stefan Behnel wrote:
 I propose to split the extension module initialisation into two steps in
 Python 3.4, in a backwards compatible way.

 Step 1: The current module init function can be reduced to just creating
 the module instance and returning it (and potentially doing some simple
>> C
 level setup). Optionally, after creating the module (and this is the new
 part), the module init code can register a C callback function that
>> will be
 called after setting up the module.
>>>
>>> Why even bother with the module creation? Why can't Python do that as
>> well
>>> and then call the callback?
>>>
>>>
 Step 2: The shared library importer receives the module instance from
>> the
 module init function, adds __file__, __path__, __package__ and friends
>> to
 the module dict, and then checks for the callback. If non-NULL, it
>> calls it
 to continue the module initialisation by user code.
>>> [...]
>>> An alternative to the alternative is that if the PyInit2 function exists
>>> it's called instead of the the PyInit function, and then the PyInit
>>> function is nothing more than a single line function call (or whatever
>> the
>>> absolute bare minimum is) into some helper that calls the PyInit2 call
>>> properly for backwards ABI compatibility (i.e. passes in whatever details
>>> are lost by the indirection in function call). That provides an eventual
>>> upgrade path of dropping PyInit and moving over to PyInit2.
>>
>> In that case, you'd have to export the PyModuleDef descriptor as well,
>> because that's what tells CPython how the module behaves and what to do
>> with it to set it up properly (e.g. allocate module state space on the
>> heap).
> 
> True.
> 
>> In fact, if the module init function became a field in the descriptor, it
>> would be enough (taking backwards compatibility aside) if *only* the
>> descriptor was exported and used by the module loader.
>
> Also true.
> 
>> With the caveat that this might kill some less common but not necessarily
>> illegitimate use cases that do more than just creating and initialising a
>> single module...
> 
> You mean creating another module in the init function? That's fine, but
> that should be a call to __import__ anyway and that should handle things
> properly.

Ok.


> Else you are circumventing the import system and you can do
> everything from scratch.

I guess I'd be ok with putting that burden on users in this case.


> I don't see why this would stop you from doing
> anything you want, it just simplifies the common case.

The only problematic case I see here would be a module that calculates the
size of its state space at init time, e.g. based on some platform specifics
or environment parameters, anything from the platform specific size of some
data type to the runtime configured number of OpenMP threads.

That would make the PyModuleDef a compile time static thing - not sure if
that's currently required.

Stefan


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Update - Re: Make extension module initialisation more like Python module initialisation

2012-11-08 Thread Nick Coghlan
On Fri, Nov 9, 2012 at 12:32 AM, Stefan Behnel  wrote:

> here's an updated proposal, adopting Marc-Andre's improvement that uses a
> new field in the PyModuleDef struct to register the callback. Note that
> this change no longer keeps up binary compatibility, which may or may not
> be acceptable for Python 3.4.
>

It's not acceptable, as PyModuleDef is part of PEP 384's stable ABI. All
such public structures are locked at their original size.

3) The original proposal used a new C-API function to register the callback
> explicitly, as opposed to extending the PyModuleDef struct. This has the
> advantage of keeping up binary compatibility with existing Py3.3
> extensions. It has the disadvantage of adding another indirection to the
> setup procedure where a static function pointer would suffice.
>

Module initialisation is (and must be) part of the stable ABI. Indirection
(especially through Python) is a *good* thing, as, ideally, any new
interfaces should be defined in a way that doesn't increase the maintenance
burden for the stable ABI.

I don't agree that the use of a new init API can fail silently, so long as
it completely *replaces* the old API, rather than being an addition. That
way, since you won't be defining the *old* init function at all, old
versions will correctly refuse to load your module.

So I propose that we simply *fix* extension module loading to work the same
way as everything else: the loader creates the module object, and passes it
in to a new init function to be fully populated. __file__ and __name__
would be passed in as preinitialised module attributes. The existing
PyModule_Create functions would be complemented by a PyModule_SetDef
function which allowed a PyModuleDef to be configured on a pre-existing
module.

Extension modules that wanted to create multiple Python modules would still
be free to do so - it would just be up to the extension initialisation code
to call PyModule_Create to construct them and set __file__ based on the
__file__ of the passed in module.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Tres Seaver
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

While porting the BTrees package (split out from ZODB) to Python3., my
first step is to get the pure-Python reference implementation working:
in order to do that, I need a way to check that objects used as keys are
*not* using the comparison semantics inherited from the base 'object'
class, because those semantics rely on properties which are not stable
across the lifetime of the (persisted / restored) key.

The existing C code does something like::

 static int
 check_argument_cmp(PyObject *arg)
 {
if (arg->ob_type->tp_richcompare == NULL
   && arg->ob_type->tp_compare ==
((PyTypeObject *)object_)->ob_type->tp_compare)
 {
   PyErr_SetString(PyExc_TypeError, "Object has default comparison");
   return 0;
 }
   return 1;
 }

Unless I'm mistaken, there is no way to do the equivalent from pure
Python.. I tried a couple of approximations which relied on non-API
attributes (I"m looking at out, methodwrapper.__objclass__), but without
much success (and I need the code to work on PyPy / Jython, too).

Am I missing something?



Tres.
- -- 
===
Tres Seaver  +1 540-429-0999  tsea...@palladion.com
Palladion Software   "Excellence by Design"http://palladion.com
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCb21gACgkQ+gerLs4ltQ4jBACfV0lQaQ2eW2vhAtWunLUsPQWM
esEAoMYeeQvlJVnckaBg4HM19LoxPIWB
=+d+0
-END PGP SIGNATURE-

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Guido van Rossum
Well, the default behavior has changed to raise an exception when
using <, <=, >, >=; i.e., inequalities do not have a default
implementation at all. Perhaps that is enough for your purpose? == and
!= still compare by pointer, but you're primarily interested in
ordering her, right?

On Thu, Nov 8, 2012 at 8:18 AM, Tres Seaver  wrote:
> -BEGIN PGP SIGNED MESSAGE-
> Hash: SHA1
>
> While porting the BTrees package (split out from ZODB) to Python3., my
> first step is to get the pure-Python reference implementation working:
> in order to do that, I need a way to check that objects used as keys are
> *not* using the comparison semantics inherited from the base 'object'
> class, because those semantics rely on properties which are not stable
> across the lifetime of the (persisted / restored) key.
>
> The existing C code does something like::
>
>  static int
>  check_argument_cmp(PyObject *arg)
>  {
> if (arg->ob_type->tp_richcompare == NULL
>&& arg->ob_type->tp_compare ==
> ((PyTypeObject *)object_)->ob_type->tp_compare)
>  {
>PyErr_SetString(PyExc_TypeError, "Object has default comparison");
>return 0;
>  }
>return 1;
>  }
>
> Unless I'm mistaken, there is no way to do the equivalent from pure
> Python.. I tried a couple of approximations which relied on non-API
> attributes (I"m looking at out, methodwrapper.__objclass__), but without
> much success (and I need the code to work on PyPy / Jython, too).
>
> Am I missing something?
>
>
>
> Tres.
> - --
> ===
> Tres Seaver  +1 540-429-0999  tsea...@palladion.com
> Palladion Software   "Excellence by Design"http://palladion.com
> -BEGIN PGP SIGNATURE-
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://www.enigmail.net/
>
> iEYEARECAAYFAlCb21gACgkQ+gerLs4ltQ4jBACfV0lQaQ2eW2vhAtWunLUsPQWM
> esEAoMYeeQvlJVnckaBg4HM19LoxPIWB
> =+d+0
> -END PGP SIGNATURE-
>
> ___
> Python-Dev mailing list
> Python-Dev@python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe: 
> http://mail.python.org/mailman/options/python-dev/guido%40python.org



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: Issue #16218: skip test if filesystem doesn't support required encoding

2012-11-08 Thread Serhiy Storchaka

On 08.11.12 01:08, R. David Murray wrote:

Alexandre's point was that the string did not appear to be arbitrary,
but rather appeared to specifically be a string containing surrogates.
Is this not the case?


My intention was testing with filename which cannot be decoded as UTF-8 
in strict mode.  I agree that testing with name which is encodable in 
locale encoding can be useful too, but now the test has no effect on 
UTF-8 locale.



___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Tres Seaver
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

On 11/08/2012 12:38 PM, Guido van Rossum wrote:
> Well, the default behavior has changed to raise an exception when 
> using <, <=, >, >=; i.e., inequalities do not have a default 
> implementation at all. Perhaps that is enough for your purpose? ==
> and != still compare by pointer, but you're primarily interested in 
> ordering her, right?

The b-search algorithm needs ordering on keys, but also meaningful
(non-identity) equality.


Tres.
- -- 
===
Tres Seaver  +1 540-429-0999  tsea...@palladion.com
Palladion Software   "Excellence by Design"http://palladion.com
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCb8iMACgkQ+gerLs4ltQ6WOACgwERmICWr80qnEoOVVLQtFwTH
ttAAnAt4An0dPjaRuZyJlDAUGzH0CS7B
=u2mG
-END PGP SIGNATURE-

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Guido van Rossum
In Python, you can write

if C.__eq__ == object.__eq__:
print('class C does not override __eq__')

Does that help?

On Thu, Nov 8, 2012 at 9:55 AM, Tres Seaver  wrote:
> -BEGIN PGP SIGNED MESSAGE-
> Hash: SHA1
>
> On 11/08/2012 12:38 PM, Guido van Rossum wrote:
>> Well, the default behavior has changed to raise an exception when
>> using <, <=, >, >=; i.e., inequalities do not have a default
>> implementation at all. Perhaps that is enough for your purpose? ==
>> and != still compare by pointer, but you're primarily interested in
>> ordering her, right?
>
> The b-search algorithm needs ordering on keys, but also meaningful
> (non-identity) equality.
>
>
> Tres.
> - --
> ===
> Tres Seaver  +1 540-429-0999  tsea...@palladion.com
> Palladion Software   "Excellence by Design"http://palladion.com
> -BEGIN PGP SIGNATURE-
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://www.enigmail.net/
>
> iEYEARECAAYFAlCb8iMACgkQ+gerLs4ltQ6WOACgwERmICWr80qnEoOVVLQtFwTH
> ttAAnAt4An0dPjaRuZyJlDAUGzH0CS7B
> =u2mG
> -END PGP SIGNATURE-



-- 
--Guido van Rossum (python.org/~guido)
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] chained assignment weirdity

2012-11-08 Thread Serhiy Storchaka
On 08.11.12 03:11, Ned Batchelder wrote:
> Sorry, I should have been clearer: I was asking about weird not to say, 
> "This is weird and should be changed!", but to get clarification from 
> Serhiy about his statement, " It will be weird if a dict comprehension 
> and a plain loop will be inconsistent."  I honestly didn't know which 
> behavior he considered inconsistent and therefore weird.

I was referring to two of the most popular idioms to dynamically create a dict.

  d = {}
  for x in a:
  d[k(x)] = v(x)

  d = {k(x): v(x) for x in a}

For now these methods are consistent.

I agree that the use of the side effects here is not a sensible idea, but when 
such effects occur by accident, it will cause a surprise.


___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: Issue #16218: skip test if filesystem doesn't support required encoding

2012-11-08 Thread Alexandre Vassalotti
On Thu, Nov 8, 2012 at 9:45 AM, Serhiy Storchaka wrote:

> My intention was testing with filename which cannot be decoded as UTF-8 in
> strict mode.  I agree that testing with name which is encodable in locale
> encoding can be useful too, but now the test has no effect on UTF-8 locale.


So should we change the test back? Or just change the test name?
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: Issue #16218: skip test if filesystem doesn't support required encoding

2012-11-08 Thread Serhiy Storchaka

On 08.11.12 20:10, Alexandre Vassalotti wrote:

So should we change the test back? Or just change the test name?


No. I also missed some details. The issue is still not closed.

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Tres Seaver
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

On 11/08/2012 12:59 PM, Guido van Rossum wrote:
> In Python, you can write
> 
> if C.__eq__ == object.__eq__: print('class C does not override
> __eq__')
> 
> Does that help?

That works in Python3, but not in Python2 -- methodwrappers don't compare
equal the same way slotwrappers do :(.  I still need to straddle (back to
Python 2.6.



Tres.
- -- 
===
Tres Seaver  +1 540-429-0999  tsea...@palladion.com
Palladion Software   "Excellence by Design"http://palladion.com
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCcDVoACgkQ+gerLs4ltQ49kwCgtBJX1oyi5cudZeE4LBEdcAmO
aTAAn0jL56d3eK/sDUD3G7zzZ/ZdQH4W
=NG86
-END PGP SIGNATURE-

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Detecting tp_compare / tp_richcompare from Python

2012-11-08 Thread Tres Seaver
-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1

On 11/08/2012 02:51 PM, Tres Seaver wrote:
> On 11/08/2012 12:59 PM, Guido van Rossum wrote:
>> In Python, you can write
> 
>> if C.__eq__ == object.__eq__: print('class C does not override 
>> __eq__')
> 
>> Does that help?
> 
> That works in Python3, but not in Python2 -- methodwrappers don't
> compare equal the same way slotwrappers do :(.  I still need to
> straddle (back to Python 2.6.

I think I'm going to need two separate checkers:

- - In Py3k, use your check above for both '__eq__' and '__lt__', e.g:

   def check_comparable(key);
   klass = type(key)
   if klass.__eq__ == object.__eq__ or klass.__lt__ == object.__lt__:
   raise TypeError('Incomparable')

- - In Python2, I think I can just check that the instance (not its class)
  has either ('__lt__' and '__eq__') *or* '__cmp__', e.g.:

   def check_comparable(key);
   if not ((getattr(key, '__eq__', None) is not None and
getattr(key, '__lt__', None) is not None) or
getattr(key, '__cmp__', None) is not None):
  raise TypeError('Incomparable')



Tres.
- -- 
===
Tres Seaver  +1 540-429-0999  tsea...@palladion.com
Palladion Software   "Excellence by Design"http://palladion.com
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://www.enigmail.net/

iEYEARECAAYFAlCcFrcACgkQ+gerLs4ltQ6JDgCeJpDmo70H9Xgz3preNHVvaTl5
dYkAoNS+fpSXwZiaD2J2ONyQZ2mPIcFC
=OiMY
-END PGP SIGNATURE-

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: issue9584: Add {} list expansion to glob. Original patch by Mathieu Bridon

2012-11-08 Thread Georg Brandl
On 11/06/2012 02:56 PM, tim.golden wrote:
> http://hg.python.org/cpython/rev/dafca4714298
> changeset:   80273:dafca4714298
> user:Tim Golden 
> date:Tue Nov 06 13:50:42 2012 +
> summary:
>   issue9584: Add {} list expansion to glob. Original patch by Mathieu Bridon
> 
> files:
>   Doc/library/glob.rst  |  11 ++--
>   Lib/glob.py   |  65 ++
>   Lib/test/test_glob.py |  64 +-
>   Misc/NEWS |   3 +
>   4 files changed, 118 insertions(+), 25 deletions(-)
> 
> 
> diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst
> --- a/Doc/library/glob.rst
> +++ b/Doc/library/glob.rst
> @@ -13,10 +13,10 @@
>  
>  The :mod:`glob` module finds all the pathnames matching a specified pattern
>  according to the rules used by the Unix shell.  No tilde expansion is done, 
> but
> -``*``, ``?``, and character ranges expressed with ``[]`` will be correctly
> -matched.  This is done by using the :func:`os.listdir` and
> -:func:`fnmatch.fnmatch` functions in concert, and not by actually invoking a
> -subshell.  (For tilde and shell variable expansion, use
> +``*``, ``?``, character ranges expressed with ``[]`` and list of options
> +expressed with ``{}`` will be correctly matched.  This is done by using the
> +:func:`os.listdir` and :func:`fnmatch.fnmatch` functions in concert, and not 
> by
> +actually invoking a subshell.  (For tilde and shell variable expansion, use
>  :func:`os.path.expanduser` and :func:`os.path.expandvars`.)

Needs a versionchanged.

In any case, brace expansion is not part of globbing (see the bash or zsh
manuals) because it does not generate valid file names, and it is a non-POSIX
expansion of some shells.  Are you sure it should be put into the glob module?
(Not speaking of the backward incompatibility it creates.)

Georg

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: issue9584: Add {} list expansion to glob. Original patch by Mathieu Bridon

2012-11-08 Thread Georg Brandl
On 11/08/2012 09:43 PM, Georg Brandl wrote:
> 
> Needs a versionchanged.
> 
> In any case, brace expansion is not part of globbing (see the bash or zsh
> manuals) because it does not generate valid file names, and it is a non-POSIX
> expansion of some shells.  Are you sure it should be put into the glob module?
> (Not speaking of the backward incompatibility it creates.)

Sorry, just saw the reversion.  Never mind, my concerns have been addressed.

Georg

___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] cpython: issue9584: Add {} list expansion to glob. Original patch by Mathieu Bridon

2012-11-08 Thread Tim Golden

On 08/11/2012 20:43, Georg Brandl wrote:

On 11/06/2012 02:56 PM, tim.golden wrote:

http://hg.python.org/cpython/rev/dafca4714298
changeset:   80273:dafca4714298
user:Tim Golden 
date:Tue Nov 06 13:50:42 2012 +
summary:
   issue9584: Add {} list expansion to glob. Original patch by Mathieu Bridon

files:
   Doc/library/glob.rst  |  11 ++--
   Lib/glob.py   |  65 ++
   Lib/test/test_glob.py |  64 +-
   Misc/NEWS |   3 +
   4 files changed, 118 insertions(+), 25 deletions(-)


diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst
--- a/Doc/library/glob.rst
+++ b/Doc/library/glob.rst
@@ -13,10 +13,10 @@

  The :mod:`glob` module finds all the pathnames matching a specified pattern
  according to the rules used by the Unix shell.  No tilde expansion is done, 
but
-``*``, ``?``, and character ranges expressed with ``[]`` will be correctly
-matched.  This is done by using the :func:`os.listdir` and
-:func:`fnmatch.fnmatch` functions in concert, and not by actually invoking a
-subshell.  (For tilde and shell variable expansion, use
+``*``, ``?``, character ranges expressed with ``[]`` and list of options
+expressed with ``{}`` will be correctly matched.  This is done by using the
+:func:`os.listdir` and :func:`fnmatch.fnmatch` functions in concert, and not by
+actually invoking a subshell.  (For tilde and shell variable expansion, use
  :func:`os.path.expanduser` and :func:`os.path.expandvars`.)


Needs a versionchanged.

In any case, brace expansion is not part of globbing (see the bash or zsh
manuals) because it does not generate valid file names, and it is a non-POSIX
expansion of some shells.  Are you sure it should be put into the glob module?
(Not speaking of the backward incompatibility it creates.)


I backed it out very soon afterwards, Georg. It had had some (quite a 
bit of) discussion on the issue, but I'd messed up the patch somehow and 
the backwards compat issue was raised pretty much immediately by Serhiy. 
So I pulled the commit.


TJG

Insofar as
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com