You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

263 lines
9.9 KiB

..
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
..
Python API conventions
######################
:summary: Basic rules and good practices for both Python binding developers and
users.
:ref-prefix:
corrade
magnum
`API naming`_
=============
- mapping is made as clear as possible, the user should not need assistance
to know what's the corresponding name in Python
- modules ``lowercase``, words *not* separated with underscores (which means
C++ namespaces have to be named clearly and tersely to make this possible)
- class names ``CamelCase``
- function names ``snake_case``
- constants and enums ``UPPERCASE``, again underscores omitted if it doesn't
hurt readability
`Preprocessor definitions`_
---------------------------
Exposed to Python as plain boolean constants, and only those that actually are
useful in a Python setting.
.. class:: m-table
=================================== ==================================
C++ Python
=================================== ==================================
:dox:`CORRADE_BUILD_MULTITHREADED` :ref:`corrade.BUILD_MULTITHREADED`
:dox:`MAGNUM_TARGET_GLES` :ref:`magnum.TARGET_GLES`
=================================== ==================================
`Namespaces / modules`_
-----------------------
.. class:: m-table
=================================== ============================
C++ Python
=================================== ============================
:dox:`Magnum::Math` :ref:`magnum.math`
:dox:`Magnum::SceneGraph` :ref:`magnum.scenegraph`
=================================== ============================
`Classes`_
----------
.. class:: m-table
=================================== ============================
C++ Python
=================================== ============================
:dox:`Vector2i` :ref:`Vector2i`
:dox:`GL::Buffer` :ref:`gl.Buffer`
=================================== ============================
`Functions`_
------------
.. class:: m-table
=============================================================== ===========
C++ Python
=============================================================== ===========
:dox:`Math::angle()` :ref:`math.angle()`
:dox:`Vector2::xAxis() <Math::Vector2::xAxis()>` :ref:`Vector2.x_axis()`
:dox:`v.isZero() <Math::Vector::isZero()>` :ref:`v.is_zero() <Vector3.is_zero()>`
:dox:`m.transformVector(a) <Math::Matrix4::transformVector()>` :ref:`m.transform_vector(a) <Matrix4.transform_vector()>`
=============================================================== ===========
`Enums`_
--------
Custom construction helpers for enums are converted to functions directly on
the Python enum class.
.. class:: m-table
============================================== ============================
C++ Python
============================================== ============================
:dox:`PixelFormat::RGB8Unorm` :ref:`PixelFormat.RGB8_UNORM`
:dox:`MeshPrimitive::TriangleStrip` :ref:`MeshPrimitive.TRIANGLE_STRIP`
:dox:`Trade::meshAttributeCustom()` :ref:`trade.MeshAttribute.CUSTOM() <trade.MeshAttribute>`
:dox:`Trade::isMeshAttributeCustom()` :ref:`trade.MeshAttribute.is_custom <trade.MeshAttribute>`
============================================== ============================
`Enum sets`_
------------
Compared to C++, there's just one enum type with a plural name, and it contains
both the values and binary operators. Additionally, there's an explicit
``NONE`` value for an empty set.
.. class:: m-table
============================================== ============================
C++ Python
============================================== ============================
:dox:`Trade::DataFlag::Mutable` :ref:`trade.DataFlags.MUTABLE`
:dox:`Trade::DataFlags{} <Trade::DataFlags>` :ref:`trade.DataFlags.NONE`
============================================== ============================
`Constants`_
------------
Apart from :dox:`Math::Constants`, which are exposed directly as members of the
:ref:`magnum.math` submodule to mimic Python's :ref:`math`, most of the
constants used throughout the C++ API are related to templates. Those are,
where applicable, converted to Python builtins such as :py:`len()`.
.. class:: m-table
============================================== ============================
C++ Python
============================================== ============================
:dox:`Constants::pi() <Math::Constants::pi()>` :ref:`math.pi <magnum.math.pi>`
:dox:`Math::Vector::Size` :py:`len(vec)`
============================================== ============================
`Initialization tags`_
----------------------
Since overloading based on argument types is not a common thing to do in Python
(and it adds extra overhead in pybind11), all initialization tags are converted
to static constructors instead:
.. container:: m-row
.. container:: m-col-m-6
.. code-figure::
.. code:: c++
Matrix4 a{Math::IdentityInit, 5.0f};
GL::Buffer b{NoCreate};
C++
.. container:: m-col-m-6
.. code-figure::
.. code:: py
a = Matrix4.identity_init(5.0)
b = gl.Buffer.no_create()
Python
There's no equivalent for the :dox:`Math::NoInit <Math::NoInitT>` tag, as
such optimization doesn't make much sense when instances are copied back
and forth between C++ and Python. Similarly, the :dox:`NoCreate <NoCreateT>`
tag makes sense only in C++ which differentiates between stack-allocated and
heap-allocated instances. In Python it's enough to simply set an instance to
:py:`None` to achieve the same effect.
`Name import conventions`_
==========================
Similarly to C++, where it's encouraged to do something like
.. code:: c++
namespace YourProject {
using namespace Magnum;
}
and then use Magnum C++ APIs unprefixed from inside that namespace, the
recommended Python workflow is similar. Note that importing the root module
*does not* import submodules, so you are expected to import those on an
as-needed basis as well.
.. code:: py
from magnum import *
from magnum import gl, platform
In particular, both the C++ and the Python API is designed in a way to prevent
too generic or confusing names in the root namespace / module and also keeping
it relatively clean and small, without too many symbols. On the other hand, the
subnamespaces *do* have generic names. The :dox:`GL::version()` /
:ref:`gl.version()` API is one example --- it's tucked in a subnamespace so the
generic name isn't a problem, but you wouldn't find anything of similar
genericity in the root namespace / module.
An exception to this rule is exposed preprocessor definitions --- these are
*not* pulled in when doing :py:`from magnum import *` as this would likely
cause conflicts (in particular, :ref:`BUILD_STATIC` is defined by Corrade as
well). Instead, you have to access them like this:
.. code:: py
import magnum
if magnum.TARGET_GLES2:
format = gl.TextureFormat.RGBA8
else:
format = gl.TextureFormat.R8
`Handling of alternate implementations`_
----------------------------------------
C++ APIs that have alternative implementations (such as
:dox:`Platform::Sdl2Application` vs. :dox:`Platform::GlfwApplication`, or
:dox:`SceneGraph::MatrixTransformation3D` vs.
:dox:`SceneGraph::TranslationRotationScalingTransformation3D`) either provide
:cpp:`typedef`\ s based on what header you include or require you to
:cpp:`typedef` them yourselves:
.. code:: c++
class MyApplication: Platform::Application {}; // depends on what you include
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
In Python, the alternate implementations are tucked in submodules (such as
:ref:`platform.sdl2` vs. :ref:`platform.glfw`, or :ref:`scenegraph.matrix` vs.
:ref:`scenegraph.trs`), each submodule providing the same names (such as
:ref:`Application <platform.sdl2.Application>` or
:ref:`Object3D <scenegraph.matrix.Object3D>`)
and the designed way to use them is via :py:`from ... import`:
.. code:: py
from magnum.platform.sdl2 import Application
from magnum.scenegraph.trs import Scene3D, Object3D
`Basic guarantees`_
===================
- All types printable using :dox:`Utility::Debug` implement :py:`__repr__()`
on the Python side, producing the exact same output.