Skip to content

Commit f3bfa3c

Browse files
committed
Convert back to RST, add section on runtime loading
1 parent 11a9428 commit f3bfa3c

11 files changed

+787
-657
lines changed

doc/requirements.txt

-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ sphinx
44
furo>=2022.9.15
55
pygments>=2.7
66

7-
# Markdown
8-
myst-parser
9-
107
# C# via doxygen
118
breathe
129
git+https://github.com/rogerbarton/sphinx-csharp.git

doc/source/codecs.rst

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
.. _codecs:
2+
3+
Codecs
4+
======
5+
6+
Python.NET performs some conversions between .NET and Python automatically.
7+
For example, when Python calls this C# method:
8+
9+
.. code:: csharp
10+
11+
void Foo(int bar) { ... }
12+
13+
via ``Foo(42)``, Python value ``42`` of type ``int`` will be
14+
automatically converted to .NET type ``System.Int32``. Another way to
15+
invoke those conversions is to call ``dotNetObject.ToPython()``
16+
(available as an extension method) or ``pyObject.As<T>()`` to convert
17+
``PyObject`` to .NET.
18+
19+
An incomplete list of Python types, that are converted between Python
20+
and .NET automatically: most numeric types, ``bool``, ``string``,
21+
``Nullable<T>`` to its ``Value`` or ``None`` and back, etc.
22+
23+
A custom conversion (**Codec**) can be defined by implementing one of the (or
24+
both) interfaces:
25+
26+
- ``Python.Runtime.IPyObjectDecoder`` to marshal Python objects to .NET
27+
28+
.. code:: csharp
29+
30+
interface IPyObjectDecoder {
31+
bool CanDecode(PyObject objectType, System.Type targetType);
32+
bool TryDecode<T>(PyObject pyObj, out T value);
33+
}
34+
35+
- ``Python.Runtime.IPyObjectEncoder`` to marshal .NET objects to Python
36+
37+
.. code:: csharp
38+
39+
interface IPyObjectEncoder {
40+
bool CanEncode(System.Type);
41+
PyObject TryEncode(System.Object);
42+
}
43+
44+
Once implemented, instances have to be registered with
45+
``Python.Runtime.PyObjectConversions.RegisterEncoder``/``-Decoder``. One
46+
can override *some* of the default conversions by registering new
47+
codecs.
48+
49+
Codec priorities
50+
~~~~~~~~~~~~~~~~
51+
52+
When multiple codecs are registered, the runtime will first try the ones, that
53+
were registered earlier. If you need to have some grouping of codecs by
54+
priority, create and expose
55+
``Python.Runtime.Codecs.EncoderGroup``/``-.DecoderGroup``. For example:
56+
57+
.. code:: csharp
58+
59+
public static EncoderGroup HighPriorityEncoders{ get; } = new EncoderGroup();
60+
61+
void Init() {
62+
PyObjectConversions.RegisterEncoder(HighPriorityEncoders);
63+
var lowPriorityEncoder = new SomeEncoder();
64+
PyObjectConversions.RegisterEncoder(lowPriorityEncoder);
65+
}
66+
67+
... some time later
68+
69+
HighPriorityEncoders.Add(new SomeOtherEncoder());

doc/source/conf.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
# add these directories to sys.path here. If the directory is relative to the
1111
# documentation root, use os.path.abspath to make it absolute, like shown here.
1212
#
13-
# import os
14-
# import sys
15-
# sys.path.insert(0, os.path.abspath('.'))
13+
import os, sys
14+
sys.path.insert(0, os.path.abspath('../..'))
1615

1716

1817
# -- Project information -----------------------------------------------------
1918

20-
project = 'Python.NET'
21-
copyright = '2022, The Python.NET Project Contributors'
22-
author = 'The Python.NET Project Contributors'
19+
project = "Python.NET"
20+
copyright = "2022, The Python.NET Project Contributors"
21+
author = "The Python.NET Project Contributors"
2322

2423

2524
# -- General configuration ---------------------------------------------------
@@ -28,14 +27,14 @@
2827
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
2928
# ones.
3029
extensions = [
31-
'myst_parser',
32-
'breathe',
33-
'sphinx.ext.autodoc',
34-
'sphinx_csharp',
30+
"breathe",
31+
"sphinx.ext.autodoc",
32+
"sphinx_csharp",
33+
"sphinx.ext.intersphinx",
3534
]
3635

3736
# Add any paths that contain templates here, relative to this directory.
38-
templates_path = ['_templates']
37+
templates_path = ["_templates"]
3938

4039
# List of patterns, relative to source directory, that match files and
4140
# directories to ignore when looking for source files.
@@ -48,12 +47,14 @@
4847
# The theme to use for HTML and HTML Help pages. See the documentation for
4948
# a list of builtin themes.
5049
#
51-
html_theme = 'furo'
50+
html_theme = "furo"
5251

5352
# Add any paths that contain custom static files (such as style sheets) here,
5453
# relative to this directory. They are copied after the builtin static files,
5554
# so a file named "default.css" will overwrite the builtin "default.css".
56-
html_static_path = ['_static']
55+
html_static_path = ["_static"]
5756

58-
breathe_projects = { "pythonnet": "../doxygen_xml" }
59-
breathe_default_project = 'pythonnet'
57+
breathe_projects = {"pythonnet": "../doxygen_xml"}
58+
breathe_default_project = "pythonnet"
59+
60+
intersphinx_mapping = {"clr-loader": ("https://pythonnet.github.io/clr-loader/", None)}

doc/source/dotnet.md

-136
This file was deleted.

doc/source/dotnet.rst

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
Embedding Python into .NET
2+
==========================
3+
4+
.. warning::
5+
Because Python code running under Python.NET is inherently
6+
unverifiable, it runs totally under the radar of the security
7+
infrastructure of the CLR so you should restrict use of the Python
8+
assembly to trusted code.
9+
10+
The Python runtime assembly defines a number of public classes that
11+
provide a subset of the functionality provided by the Python C-API.
12+
13+
These classes include PyObject, PyList, PyDict, PyTuple, etc.
14+
15+
At a very high level, to embed Python in your application one will need
16+
to:
17+
18+
- Reference ``Python.Runtime.dll`` (e.g. via a ``PackageReference``)
19+
- Call ``PythonEngine.Initialize()`` to initialize Python
20+
- Call ``PythonEngine.ImportModule(name)`` to import a module
21+
22+
The module you import can either start working with your managed app
23+
environment at the time its imported, or you can explicitly lookup and
24+
call objects in a module you import.
25+
26+
For general-purpose information on embedding Python in applications, use
27+
www.python.org or Google to find (C) examples. Because Python.NET is so
28+
closely integrated with the managed environment, one will generally be
29+
better off importing a module and deferring to Python code as early as
30+
possible rather than writing a lot of managed embedding code.
31+
32+
.. note::
33+
Python is not free-threaded and uses a
34+
global interpreter lock to allow multi-threaded applications to interact
35+
safely with the Python interpreter. Much more information about this is
36+
available in the Python C-API documentation on the www.python.org
37+
Website.
38+
39+
When embedding Python in a managed application, one has to manage the
40+
GIL in just the same way you would when embedding Python in a C or C++
41+
application.
42+
43+
Before interacting with any of the objects or APIs provided by the
44+
``Python.Runtime`` namespace, calling code must have acquired the Python
45+
global interpreter lock by calling the ``PythonEngine.AcquireLock``
46+
method. The only exception to this rule is the
47+
``PythonEngine.Initialize`` method, which may be called at startup
48+
without having acquired the GIL.
49+
50+
When finished using Python APIs, managed code must call a corresponding
51+
``PythonEngine.ReleaseLock`` to release the GIL and allow other threads
52+
to use Python.
53+
54+
A ``using`` statement may be used to acquire and release the GIL:
55+
56+
.. code:: csharp
57+
58+
using (Py.GIL())
59+
{
60+
PythonEngine.Exec("doStuff()");
61+
}
62+
63+
The AcquireLock and ReleaseLock methods are thin wrappers over the
64+
unmanaged ``PyGILState_Ensure`` and ``PyGILState_Release`` functions
65+
from the Python API, and the documentation for those APIs applies to the
66+
managed versions.
67+
68+
Passing C# Objects to the Python Engine
69+
---------------------------------------
70+
71+
This section demonstrates how to pass a C# object to the Python runtime.
72+
The example uses the following ``Person`` class:
73+
74+
.. code:: csharp
75+
76+
public class Person
77+
{
78+
public Person(string firstName, string lastName)
79+
{
80+
FirstName = firstName;
81+
LastName = lastName;
82+
}
83+
84+
public string FirstName { get; set; }
85+
public string LastName { get; set; }
86+
}
87+
88+
In order to pass a C# object to the Python runtime, it must be converted
89+
to a ``PyObject``. This is done using the ``ToPython()`` extension
90+
method. The ``PyObject`` may then be set as a variable in a ``PyScope``.
91+
Code executed from the scope will have access to the variable:
92+
93+
.. code:: csharp
94+
95+
// create a person object
96+
Person person = new Person("John", "Smith");
97+
98+
// acquire the GIL before using the Python interpreter
99+
using (Py.GIL())
100+
{
101+
// create a Python scope
102+
using (PyScope scope = Py.CreateScope())
103+
{
104+
// convert the Person object to a PyObject
105+
PyObject pyPerson = person.ToPython();
106+
107+
// create a Python variable "person"
108+
scope.Set("person", pyPerson);
109+
110+
// the person object may now be used in Python
111+
string code = "fullName = person.FirstName + ' ' + person.LastName";
112+
scope.Exec(code);
113+
}
114+
}

0 commit comments

Comments
 (0)