Skip to content

Commit b6abf1a

Browse files
committed
Editing docs from handwritten notes. Up to page 98.
1 parent a6be6c0 commit b6abf1a

File tree

6 files changed

+76
-24
lines changed

6 files changed

+76
-24
lines changed

doc/sphinx/source/canonical_function.rst

+6
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ Notice the ``except:`` block falls through to the ``finally:`` block.
150150
.. index::
151151
single: C Functions; Full Coding Pattern
152152

153+
.. raw:: latex
154+
155+
[Continued on the next page]
156+
157+
\pagebreak
158+
153159
The Function Code as One
154160
========================
155161

doc/sphinx/source/capsules.rst

+23-19
Original file line numberDiff line numberDiff line change
@@ -366,15 +366,15 @@ In this case we want to create a subclass of the ``datetime.datetime`` object th
366366
`naive` datetimes.
367367

368368
Here is the C Extension code to create a ``datetimetz`` module and a ``datetimetz.datetimetz`` object.
369-
This code is lightly edited for clarity and works with Python 3.10+.
370-
The actual code is in ``src/cpy/Capsules/datetimetz.c`` (which works with Python 3.9 as well)
369+
This code is lightly edited for clarity and works with Python 3.10+ (using the modern API).
370+
The actual code is in ``src/cpy/Capsules/datetimetz.c`` (which works with the Python 3.9 API as well)
371371
and the tests are in ``tests/unit/test_c_capsules.py``.
372372

373373
--------------------------------
374374
Writing the Code for the Object
375375
--------------------------------
376376

377-
Firstly the declaration of the timezone aware datetime, it just inherits from ``datetime.datetime``:
377+
Firstly the declaration of the timezone aware datetime, it just inherits from ``datetime.datetime`` :
378378

379379
.. code-block:: c
380380
@@ -421,8 +421,8 @@ Now the code for creating a new instance:
421421
}
422422
423423
So far a new ``datetimetz`` object must be created with a ``tzinfo`` but the ``datetime.datetime`` has an API
424-
``replace`` that creates a new datetime with different properties, including ``tzinfo``.
425-
We need to guard against the user trying to change the timezone.
424+
``replace()`` that creates a new datetime with different properties, including ``tzinfo``.
425+
We need to guard against the user trying to change the timezone to None.
426426
To do this we call the super class function and then check, and raise, if a ``tzinfo`` is absent.
427427
This uses the utility function that call Python's ``super()`` function.
428428
That code is in ``src/cpy/Util/py_call_super.h`` and ``src/cpy/Util/py_call_super.c``:
@@ -473,6 +473,10 @@ Finally the module code:
473473
.m_size = -1,
474474
};
475475
476+
Initialise the module, this is when we use the existing capsule:
477+
478+
.. code-block:: c
479+
476480
PyMODINIT_FUNC
477481
PyInit_datetimetz(void) {
478482
PyObject *m = PyModule_Create(&datetimetzmodule);
@@ -635,6 +639,19 @@ The error is handled correctly by the superclass.
635639
d.tzinfo = None
636640
assert err.value.args[0] == "attribute 'tzinfo' of 'datetime.datetime' objects is not writable"
637641
642+
Check that ``.replace()`` works as expected with ``tzinfo``:
643+
644+
.. code-block:: python
645+
646+
def test_datetimetz_datetimetz_replace_raises_tzinfo():
647+
d = datetimetz.datetimetz(
648+
2024, 7, 15, 10, 21, 14,
649+
tzinfo=zoneinfo.ZoneInfo('Europe/London')
650+
)
651+
with pytest.raises(TypeError) as err:
652+
d.replace(tzinfo=None)
653+
assert err.value.args[0] == 'No time zone provided.'
654+
638655
Some equality tests.
639656
We want to fail when comparing our ``datetimetz`` with a naive ``datatime`` object.
640657

@@ -659,7 +676,7 @@ We want to fail when comparing our ``datetimetz`` with a naive ``datatime`` obje
659676
d_no_tz = datetime.datetime(2024, 7, 15, 10, 21, 14)
660677
assert d_no_tz != d
661678
662-
Some datetime comparison tests that show our ``datetimetz`` inter-operates correctly with itself and a ``datetime``
679+
Some datetime subtraction tests that show our ``datetimetz`` inter-operates correctly with itself and a ``datetime``
663680
object.
664681

665682
.. code-block:: python
@@ -724,16 +741,3 @@ object.
724741
d_tz - d
725742
assert err.value.args[0] == "can't subtract offset-naive and offset-aware datetimes"
726743
727-
Check that ``.replace()`` works as expected with ``tzinfo``:
728-
729-
.. code-block:: python
730-
731-
def test_datetimetz_datetimetz_replace_raises_tzinfo():
732-
d = datetimetz.datetimetz(
733-
2024, 7, 15, 10, 21, 14,
734-
tzinfo=zoneinfo.ZoneInfo('Europe/London')
735-
)
736-
with pytest.raises(TypeError) as err:
737-
d.replace(tzinfo=None)
738-
assert err.value.args[0] == 'No time zone provided.'
739-

doc/sphinx/source/exceptions.rst

+37-2
Original file line numberDiff line numberDiff line change
@@ -159,19 +159,33 @@ Creating Specialised Exceptions
159159
---------------------------------
160160

161161
Often you need to create an Exception class that is specialised to a particular module.
162+
The following C code is equivalent to the Python code:
163+
164+
.. code-block:: python
165+
166+
class ExceptionBase(Exception):
167+
pass
168+
169+
class SpecialisedError(ExceptionBase):
170+
pass
171+
162172
This can be done quite easily using either the ``PyErr_NewException`` or the ``PyErr_NewExceptionWithDoc`` functions.
163173
These create new exception classes that can be added to a module.
164174
For example:
165175

166176
.. code-block:: c
167177
168178
/** Specialise exceptions base exception. */
169-
static PyObject *ExceptionBase = 0;
179+
static PyObject *ExceptionBase = NULL;
170180
/** Specialise exceptions derived from base exception. */
171-
static PyObject *SpecialisedError = 0;
181+
static PyObject *SpecialisedError = NULL;
172182
173183
/* NOTE: Functions that might raise one of these exceptions will go here. See below. */
174184
185+
Now define the module, ``cExceptions_methods`` is explained later:
186+
187+
.. code-block:: c
188+
175189
static PyModuleDef cExceptions_module = {
176190
PyModuleDef_HEAD_INIT,
177191
"cExceptions",
@@ -181,6 +195,10 @@ For example:
181195
NULL, NULL, NULL, NULL,
182196
};
183197
198+
Initialise the module, this registers the exception types and the class hierarchy:
199+
200+
.. code-block:: c
201+
184202
PyMODINIT_FUNC
185203
PyInit_cExceptions(void) {
186204
PyObject *m = PyModule_Create(&cExceptions_module);
@@ -294,3 +312,20 @@ Or fish it out of the module (this will be slower):
294312
}
295313
return NULL;
296314
}
315+
316+
Here is some test code from ``tests/unit/test_c_exceptions.py``:
317+
318+
.. code-block:: python
319+
320+
from cPyExtPatt import cExceptions
321+
322+
def test_raise_exception_base():
323+
with pytest.raises(cExceptions.ExceptionBase) as err:
324+
cExceptions.raise_exception_base()
325+
assert err.value.args[0] == 'One 1 two 2 three 3.'
326+
327+
328+
def test_raise_specialised_error():
329+
with pytest.raises(cExceptions.SpecialisedError) as err:
330+
cExceptions.raise_specialised_error()
331+
assert err.value.args[0] == 'One 1 two 2 three 3.'

doc/sphinx/source/module_globals.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ The dict is added in a separate C function merely for readability:
131131
Py_XDECREF(val);
132132
key = PyBytes_FromString("123");
133133
val = PyLong_FromLong(123);
134-
if (PyDict_SetItem(pMap, PyBytes_FromString("123"), PyLong_FromLong(123))) {
134+
if (PyDict_SetItem(pMap, key, value)) {
135135
goto except;
136136
}
137137
Py_XDECREF(key);

doc/sphinx/source/parsing_arguments.rst

+7
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ The complete C code is:
532532
return Py_BuildValue("y#", arg.buf, arg.len);
533533
}
534534
535+
See ``tests/unit/test_c_parse_args.py`` for some Python uses of this code.
536+
535537
.. index::
536538
single: Parsing Arguments Example; Positional Only Arguments
537539
single: Parsing Arguments Example; Keyword Only Arguments
@@ -690,6 +692,11 @@ Being Pythonic with Default Mutable Arguments
690692

691693
If the arguments default to some C fundamental type the code above is fine.
692694
However if the arguments default to Python objects then a little more work is needed.
695+
696+
.. note::
697+
698+
See :ref:`cpp_and_cpython.handling_default_arguments` for a way of simplifying this with C++.
699+
693700
Here is a function that has a tuple and a dict as default arguments, in other words the Python signature:
694701

695702
.. code-block:: python

src/cpy/cExceptions.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ static PyObject *raise_error_overwrite(PyObject *Py_UNUSED(module)) {
5656
}
5757

5858
/** Specialise exceptions base exception. */
59-
static PyObject *ExceptionBase = 0;
59+
static PyObject *ExceptionBase = NULL;
6060
/** Specialise exceptions derived from base exception. */
61-
static PyObject *SpecialisedError = 0;
61+
static PyObject *SpecialisedError = NULL;
6262

6363

6464
/** Raises a ExceptionBase. */

0 commit comments

Comments
 (0)