Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small documentation improvements & quickstart guide #399

Merged
merged 7 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ docs-examples-tests:
cd docs/examples/simple-example && python3 setup.py --hpy-abi=universal install
cd docs/examples/mixed-example && python3 setup.py install
cd docs/examples/snippets && python3 setup.py --hpy-abi=universal install
cd docs/examples/quickstart && python3 setup.py --hpy-abi=universal install
cd docs/examples/hpytype-example && python3 setup.py --hpy-abi=universal install
python3 -m pytest docs/examples/tests.py ${TEST_ARGS}
5 changes: 5 additions & 0 deletions docs/api-reference/inline-helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ Inline Helpers
Those functions are usually small convenience functions that everyone could
write but in order to avoid duplicated effort, they are defined by HPy.

One category of inline helpers are functions that convert the commonly used
but not fixed width C types, such as ``int``, or ``long long``, to HPy API.
The HPy API always uses well-defined fixed width types like ``int32`` or
``unsigned int8``.

.. autocmodule:: hpy/inline_helpers.h
:members:
72 changes: 44 additions & 28 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ Moreover, ``HPyContext`` is used by the :term:`HPy Universal ABI` to contain a
sort of virtual function table which is used by the C extensions to call back
into the Python interpreter.

.. _simple example:

A simple example
-----------------
Expand Down Expand Up @@ -242,9 +243,11 @@ are still written using the ``Python.h`` API.

Note that the HPy module does not specify its name. HPy does not support the legacy
single phase module initialization and the only module initialization approach is
the multi-phase initialization (PEP 451). With multi-phase module initialization,
the name of the module is always taken from the ``ModuleSpec``, i.e., most likely
from the name used in the ``import {{name}}`` statement that imported your module.
the multi-phase initialization (`PEP 489 <https://peps.python.org/pep-0489/>`_).
With multi-phase module initialization,
the name of the module is always taken from the ``ModuleSpec`` (`PEP 451 <https://peps.python.org/pep-0451/>`_)
, i.e., most likely from the name used in the ``import {{name}}`` statement that
imported your module.

This is the only difference stemming from multi-phase module initialization in this
simple example.
Expand Down Expand Up @@ -320,30 +323,6 @@ table, which now becomes:
:start-after: // BEGIN: methodsdef
:end-before: // END: methodsdef

More Examples
-------------

HPy usually has tests for each API function. This means that there is lots of
examples available by looking at the tests. However, the test source uses
many macros and is hard to read. To overcome this we supply a utility to
export clean C sources for the tests. Since the HPy tests are not shipped by
default, you need to clone the HPy repository from GitHub:

.. code-block:: console

> git clone https://github.com/hpyproject/hpy.git

After that, install all test requirements and dump the sources:

.. code-block:: console

> cd hpy
> python3 -m pip install pytest filelock
> python3 -m pytest --dump-dir=test_sources test/

This will dump the generated test sources into folder ``test_sources``. Note,
that the tests won't be executed but skipped with an appropriate message.

Creating types in HPy
---------------------

Expand Down Expand Up @@ -477,7 +456,7 @@ A type with ``.legacy_slots != NULL`` is required to have
``HPyType_BuiltinShape_Legacy`` and to include ``PyObject_HEAD`` at the start of
its struct. It would be easy to relax this requirement on CPython (where the
``PyObject_HEAD`` fields are always present) but a large burden on other
implementations (e.g. PyPy, GraalPython) where a struct starting with
implementations (e.g. PyPy, GraalPy) where a struct starting with
``PyObject_HEAD`` might not exist.

Types created via the old Python C API are automatically legacy types.
Expand Down Expand Up @@ -567,3 +546,40 @@ be considered in three places:
For more information about the built-in shape and for a technical explanation
for why it is required, see :c:member:`HPyType_Spec.builtin_shape` and
:c:enum:`HPyType_BuiltinShape`.

More Examples
-------------

The :doc:`porting-example/index` shows another complete example
of HPy extension ported from Python/C API.

The `HPy project space <https://github.com/hpyproject/>`_ on GitHub
contains forks of some popular Python extensions ported to HPy as
a proof of concept/feasibility studies, such as the
`Kiwi solver <https://github.com/hpyproject/kiwi-hpy>`_.
Note that those forks may not be up to date with their upstream projects
or with the upstream HPy changes.

HPy unit tests
~~~~~~~~~~~~~~

HPy usually has tests for each API function. This means that there is lots of
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this paragraph is just shuffled at the end of this file

examples available by looking at the tests. However, the test source uses
many macros and is hard to read. To overcome this we supply a utility to
export clean C sources for the tests. Since the HPy tests are not shipped by
default, you need to clone the HPy repository from GitHub:

.. code-block:: console

> git clone https://github.com/hpyproject/hpy.git

After that, install all test requirements and dump the sources:

.. code-block:: console

> cd hpy
> python3 -m pip install pytest filelock
> python3 -m pytest --dump-dir=test_sources test/

This will dump the generated test sources into folder ``test_sources``. Note,
that the tests won't be executed but skipped with an appropriate message.
2 changes: 1 addition & 1 deletion docs/debug-mode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Debug mode works *only* for extensions built with HPy universal ABI.
To enable debug mode, use environment variable ``HPY``. If ``HPY=debug``, then
all HPy modules are loaded with the trace context. Alternatively, it is also
possible to specify the mode per module like this:
``HPY=modA:debug,modB=debug``.
``HPY=modA:debug,modB:debug``.

In order to verify that your extension is being loaded in debug mode, use
environment variable ``HPY_LOG``. If this variable is set, then all HPy
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/hpytype-example/builtin_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ static void make_Language(HPyContext *ctx, HPy module)
}

HPyDef_SLOT(simple_exec, HPy_mod_exec)
int simple_exec_impl(HPyContext *ctx, HPy m) {
static int simple_exec_impl(HPyContext *ctx, HPy m) {
make_Dummy(ctx, m);
if (HPyErr_Occurred(ctx))
return -1;
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/hpytype-example/simple_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static HPyType_Spec Point_spec = {

// BEGIN: add_type
HPyDef_SLOT(simple_exec, HPy_mod_exec)
int simple_exec_impl(HPyContext *ctx, HPy m) {
static int simple_exec_impl(HPyContext *ctx, HPy m) {
if (!HPyHelpers_AddType(ctx, m, "Point", &Point_spec, NULL)) {
return -1;
}
Expand Down
39 changes: 39 additions & 0 deletions docs/examples/quickstart/quickstart.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// quickstart.c

// This header file is the entrypoint to the HPy API:
#include "hpy.h"

// HPy method: the HPyDef_METH macro generates some boilerplate code,
// the same code can be also written manually if desired
HPyDef_METH(say_hello, "say_hello", HPyFunc_NOARGS)
static HPy say_hello_impl(HPyContext *ctx, HPy self)
{
// Methods take HPyContext, which must be passed as the first argument to
// all HPy API functions. Other than that HPyUnicode_FromString does the
// same thing as PyUnicode_FromString.
//
// HPy type represents a "handle" to a Python object, but may not be
// a pointer to the object itself. It should be fully "opaque" to the
// users. Try uncommenting the following two lines to see the difference
// from PyObject*:
//
// if (self == self)
// HPyUnicode_FromString(ctx, "Surprise? Try HPy_Is(ctx, self, self)");

return HPyUnicode_FromString(ctx, "Hello world");
}

static HPyDef *QuickstartMethods[] = {
&say_hello, // 'say_hello' generated for us by the HPyDef_METH macro
NULL,
};

static HPyModuleDef quickstart_def = {
.doc = "HPy Quickstart Example",
.defines = QuickstartMethods,
};

// The Python interpreter will create the module for us from the
// HPyModuleDef specification. Additional initialization can be
// done in the HPy_mod_execute slot
HPy_MODINIT(quickstart, quickstart_def)
13 changes: 13 additions & 0 deletions docs/examples/quickstart/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# setup.py

from setuptools import setup, Extension
from os import path

DIR = path.dirname(__file__)
setup(
name="hpy-quickstart",
hpy_ext_modules=[
Extension('quickstart', sources=[path.join(DIR, 'quickstart.c')]),
],
setup_requires=['hpy'],
)
6 changes: 6 additions & 0 deletions docs/examples/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def test_simple_type():
assert p.z == 2000


def test_quickstart():
import quickstart
assert quickstart.say_hello() == "Hello world"
# END: test_quickstart


def test_builtin_type():
obj = builtin_type.Dummy("hello")
assert obj == "hello"
Expand Down
52 changes: 37 additions & 15 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,58 @@ HPy: a better API for Python

HPy provides a new API for extending Python in C.

There are several advantages to writing C extensions in HPy:

- **Speed**: it runs much faster on PyPy, GraalPy, and at native speed on CPython

- **Deployment**: it is possible to compile a single binary which runs unmodified on all
supported Python implementations and versions -- think "stable ABI" on steroids

- **Simplicity**: it is simpler and more manageable than the ``Python.h`` API, both for
the users and the Pythons implementing it

- **Debugging**: it provides an improved debugging experience. Debug mode can be turned
on at runtime without the need to recompile the extension or the Python running it.
HPy design is more suitable for automated checks.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I shuffled this at the top, shortened the points, and added think "stable ABI" on steroids


The official `Python/C API <https://docs.python.org/3/c-api/index.html>`_,
also informally known as ``#include <Python.h>``, is
specific to the current implementation of CPython: it exposes a lot of
internal details which makes it hard:
internal details which makes it hard to:

- implement it for other Python implementations (e.g. PyPy, GraalPy,
Jython, ...)

- experiment with new approaches inside CPython itself, for example:

- to implement it for other Python implementations (e.g. PyPy, GraalPython,
Jython, IronPython, etc.)
- use a tracing garbage collection instead of reference counting
- remove the global interpreter lock (GIL) to take full advantage of multicore architectures
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this true? Does using HPy make it any easier to be sure code does not have threading problems?

Copy link
Contributor Author

@steve-s steve-s Feb 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, good point. This bullet point was here before, I just shuffled it and added "... to take full advantage of multicore architectures".

HPy make it any easier to be sure code does not have threading problems

Depends on the point of view. My line of thinking is that it would make it easier to do the internal changes in CPython that are needed to remove the GIL. Still extensions would have to make sure that they operate correctly without GIL.

I think it still falls into the category of "what is wrong with current C API", although better HPy would not solve the problem as a whole, only a part of it. Explaining all this in detail would make this bullet point too long/complex. I like that "removing GIL" is something that many would clearly understand brings benefits and there is bit of a hype around it now :-) I think this simplification could be fine for such a pitch as the beginning of the documentation?

- use tagged pointers to reduce memory footprint

- to experiment with new things inside CPython itself: e.g. using a GC
instead of refcounting, or to remove the GIL.
Where to go next:
-----------------
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could pitch some parts of the documentation depending on what you're looking for: either code samples or some more explanations/details


There are several advantages to write your C extension in HPy:
- Show me the code:

- it runs much faster on PyPy, GraalPython, and at native speed on CPython
- :doc:`Quickstart<quickstart>`
- :ref:`Simple documented HPy extension example<simple example>`
- :doc:`Tutorial: porting Python/C API extension to HPy<porting-example/index>`

- it is possible to compile a single binary which runs unmodified on all
supported Python implementations and versions
- Details:

- it is simpler and more manageable than the ``Python.h`` API
- :doc:`HPy overview: motivation, goals, current status<overview>`
- :doc:`HPy API concepts introduction<api>`
- :doc:`Python/C API to HPy Porting guide<porting-guide>`
- :doc:`HPy API reference<api-reference/index>`

- it provides an improved debugging experience: in "debug mode", HPy
actively checks for many common mistakes such as reference leaks and
invalid usage of objects after they have been deleted. It is possible to
turn the "debug mode" on at startup time, without needing to recompile
Python or the extension itself

Full table of contents:
-----------------------

.. toctree::
:maxdepth: 2

quickstart
overview
api
porting-guide
Expand Down
4 changes: 1 addition & 3 deletions docs/misc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ Misc notes
==========

.. toctree::
:maxdepth: 2
:maxdepth: 1

str-builder-api
embedding
protocols
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two were not a real documentation, but design notes. I don't think it fits in the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an issue for the protocols: #400, and added a comment to the existing str builder issue: #214

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw this comment only after my review. Maybe we could move the string stuff to the wiki?

55 changes: 0 additions & 55 deletions docs/misc/protocols-code.c

This file was deleted.

31 changes: 0 additions & 31 deletions docs/misc/protocols.rst

This file was deleted.

Loading