Merge pull request #600 from tannewt/clarify_property_comments
Clarify style of attribute comments in the Design Guide.
This commit is contained in:
commit
446a31302c
@ -1,15 +1,20 @@
|
||||
Design Guide
|
||||
============
|
||||
|
||||
MicroPython has created a great foundation to build upon and to make it even
|
||||
better for beginners we've created CircuitPython. This guide covers a number of
|
||||
ways the core and libraries are geared towards beginners.
|
||||
This guide covers a variety of development practices for CircuitPython core and library APIs. These
|
||||
APIs are both `built-into CircuitPython
|
||||
<https://github.com/adafruit/circuitpython/tree/master/shared-bindings>`_ and those that are
|
||||
`distributed on GitHub <https://github.com/search?utf8=%E2%9C%93&q=topic%3Acircuitpython&type=>`_
|
||||
and in the `Adafruit <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_ and `Community
|
||||
<https://github.com/adafruit/CircuitPython_Community_Bundle/>`_ bundles. Consistency with these
|
||||
practices ensures that beginners can learn a pattern once and apply it throughout the CircuitPython
|
||||
ecosystem.
|
||||
|
||||
Start libraries with the cookiecutter
|
||||
-------------------------------------
|
||||
|
||||
Cookiecutter is a cool tool that lets you bootstrap a new repo based on another
|
||||
repo. We've made one `here <https://github.com/adafruit/cookiecutter-adafruit-circuitpython>`_
|
||||
Cookiecutter is a tool that lets you bootstrap a new repo based on another repo.
|
||||
We've made one `here <https://github.com/adafruit/cookiecutter-adafruit-circuitpython>`_
|
||||
for CircuitPython libraries that include configs for Travis CI and ReadTheDocs
|
||||
along with a setup.py, license, code of conduct and readme.
|
||||
|
||||
@ -20,6 +25,10 @@ along with a setup.py, license, code of conduct and readme.
|
||||
|
||||
cookiecutter gh:adafruit/cookiecutter-adafruit-circuitpython
|
||||
|
||||
Cookiecutter will provide a series of prompts relating to the library and then create a new
|
||||
directory with all of the files. See `the CircuitPython cookiecutter README
|
||||
<https://github.com/adafruit/cookiecutter-adafruit-circuitpython#introduction>`_ for more details.
|
||||
|
||||
Module Naming
|
||||
-------------
|
||||
|
||||
@ -28,7 +37,11 @@ Adafruit funded libraries should be under the
|
||||
``Adafruit_CircuitPython_<name>`` and have a corresponding ``adafruit_<name>``
|
||||
directory (aka package) or ``adafruit_<name>.py`` file (aka module).
|
||||
|
||||
Community created libraries should have the format ``CircuitPython_<name>`` and
|
||||
If the name would normally have a space, such as "Thermal Printer", use an underscore instead
|
||||
("Thermal_Printer"). This underscore will be used everywhere even when the separation between
|
||||
"adafruit" and "circuitpython" is done with a ``-``. Use the underscore in the cookiecutter prompts.
|
||||
|
||||
Community created libraries should have the repo format ``CircuitPython_<name>`` and
|
||||
not have the ``adafruit_`` module or package prefix.
|
||||
|
||||
Both should have the CircuitPython repository topic on GitHub.
|
||||
@ -90,7 +103,7 @@ Verify your device
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Whenever possible, make sure device you are talking to is the device you expect.
|
||||
If not, raise a ValueError. Beware that I2C addresses can be identical on
|
||||
If not, raise a RuntimeError. Beware that I2C addresses can be identical on
|
||||
different devices so read registers you know to make sure they match your
|
||||
expectation. Validating this upfront will help catch mistakes.
|
||||
|
||||
@ -125,6 +138,18 @@ modules to add extra functionality. By distinguishing API boundaries at modules
|
||||
you increase the likelihood that incorrect expectations are found on import and
|
||||
not randomly during runtime.
|
||||
|
||||
When adding a new module for additional functionality related to a CPython
|
||||
module do NOT simply prefix it with u. This is not a large enough differentiation
|
||||
from CPython. This is the MicroPython convention and they use u* modules
|
||||
interchangeably with the CPython name. This is confusing. Instead, think up a
|
||||
new name that is related to the extra functionality you are adding.
|
||||
|
||||
For example, storage mounting and unmounting related functions were moved from
|
||||
``uos`` into a new `storage` module. Terminal related functions were moved into
|
||||
`multiterminal`. These names better match their functionality and do not
|
||||
conflict with CPython names. Make sure to check that you don't conflict with
|
||||
CPython libraries too. That way we can port the API to CPython in the future.
|
||||
|
||||
Example
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -164,22 +189,112 @@ After the license comment::
|
||||
Class description
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Documenting what the object does::
|
||||
At the class level document what class does and how to initialize it::
|
||||
|
||||
class DS3231:
|
||||
"""Interface to the DS3231 RTC."""
|
||||
"""DS3231 real-time clock.
|
||||
|
||||
:param ~busio.I2C i2c_bus: The I2C bus the DS3231 is connected to.
|
||||
:param int address: The I2C address of the device.
|
||||
"""
|
||||
|
||||
def __init__(self, i2c_bus, address=0x40):
|
||||
self._i2c = i2c_bus
|
||||
|
||||
|
||||
Renders as:
|
||||
|
||||
.. py:class:: DS3231
|
||||
:noindex:
|
||||
.. py:class:: DS3231(i2c_bus, address=64)
|
||||
:noindex:
|
||||
|
||||
Interface to the DS3231 RTC.
|
||||
DS3231 real-time clock.
|
||||
|
||||
Data descriptor description
|
||||
:param ~busio.I2C i2c_bus: The I2C bus the DS3231 is connected to.
|
||||
:param int address: The I2C address of the device.
|
||||
|
||||
Attributes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Comment is after even though its weird::
|
||||
Attributes are state on objects. (See `Getters/Setters` above for more discussion
|
||||
about when to use them.) They can be defined internally in a number of different
|
||||
ways. Each approach is enumerated below with an explanation of where the comment
|
||||
goes.
|
||||
|
||||
Regardless of how the attribute is implemented, it should have a short
|
||||
description of what state it represents including the type, possible values and/or
|
||||
units. It should be marked as ``(read-only)`` or ``(write-only)`` at the end of
|
||||
the first line for attributes that are not both readable and writable.
|
||||
|
||||
Instance attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Comment comes from after the assignment::
|
||||
|
||||
def __init__(self, drive_mode):
|
||||
self.drive_mode = drive_mode
|
||||
"""
|
||||
The pin drive mode. One of:
|
||||
|
||||
- `digitalio.DriveMode.PUSH_PULL`
|
||||
- `digitalio.DriveMode.OPEN_DRAIN`
|
||||
"""
|
||||
|
||||
Renders as:
|
||||
|
||||
.. py:attribute:: drive_mode
|
||||
:noindex:
|
||||
|
||||
The pin drive mode. One of:
|
||||
|
||||
- `digitalio.DriveMode.PUSH_PULL`
|
||||
- `digitalio.DriveMode.OPEN_DRAIN`
|
||||
|
||||
Property description
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Comment comes from the getter::
|
||||
|
||||
@property
|
||||
def datetime(self):
|
||||
"""The current date and time as a `time.struct_time`."""
|
||||
return self.datetime_register
|
||||
|
||||
@datetime.setter
|
||||
def datetime(self, value):
|
||||
pass
|
||||
|
||||
Renders as:
|
||||
|
||||
.. py:attribute:: datetime
|
||||
:noindex:
|
||||
|
||||
The current date and time as a `time.struct_time`.
|
||||
|
||||
Read-only example::
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""
|
||||
The current temperature in degrees Celsius. (read-only)
|
||||
|
||||
The device may require calibration to get accurate readings.
|
||||
"""
|
||||
return self._read(TEMPERATURE)
|
||||
|
||||
|
||||
Renders as:
|
||||
|
||||
.. py:attribute:: temperature
|
||||
:noindex:
|
||||
|
||||
The current temperature in degrees Celsius. (read-only)
|
||||
|
||||
The device may require calibration to get accurate readings.
|
||||
|
||||
Data descriptor description
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Comment is after the definition::
|
||||
|
||||
lost_power = i2c_bit.RWBit(0x0f, 7)
|
||||
"""True if the device has lost power since the time was set."""
|
||||
@ -187,9 +302,9 @@ Comment is after even though its weird::
|
||||
Renders as:
|
||||
|
||||
.. py:attribute:: lost_power
|
||||
:noindex:
|
||||
:noindex:
|
||||
|
||||
True if the device has lost power since the time was set.
|
||||
True if the device has lost power since the time was set.
|
||||
|
||||
Method description
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -211,27 +326,6 @@ Renders as:
|
||||
|
||||
:param float degrees: Degrees to turn right
|
||||
|
||||
Property description
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Comment comes from the getter::
|
||||
|
||||
@property
|
||||
def datetime(self):
|
||||
"""The current date and time"""
|
||||
return self.datetime_register
|
||||
|
||||
@datetime.setter
|
||||
def datetime(self, value):
|
||||
pass
|
||||
|
||||
Renders as:
|
||||
|
||||
.. py:attribute:: datetime
|
||||
:noindex:
|
||||
|
||||
The current date and time
|
||||
|
||||
Use BusDevice
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -397,14 +491,14 @@ properties.
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
| ``datetime`` | time.struct | date and time |
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
|
||||
Common APIs
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Outside of sensors, having common methods amongst drivers for similar devices
|
||||
such as devices can be really useful. Its early days however. For now, try to
|
||||
adhere to guidelines in this document. Once a design is settled on, add it as a
|
||||
subsection to this one.
|
||||
| ``duty_cycle`` | int | 16-bit PWM duty cycle (regardless of output resolution) |
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
| ``frequency`` | int | Hertz |
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
| ``value`` | bool | Digital logic |
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
| ``value`` | int | 16-bit Analog value, unit-less |
|
||||
+-----------------------+-----------------------+-------------------------------------------------------------------------+
|
||||
|
||||
Adding native modules
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -106,13 +106,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogio_analogin___exit___obj, 4, 4,
|
||||
|
||||
//| .. attribute:: value
|
||||
//|
|
||||
//| Read the value on the analog pin and return it. The returned value
|
||||
//| will be between 0 and 65535 inclusive (16-bit). Even if the underlying
|
||||
//| analog to digital converter (ADC) is lower resolution, the result will
|
||||
//| be scaled to be 16-bit.
|
||||
//| The value on the analog pin between 0 and 65535 inclusive (16-bit). (read-only)
|
||||
//|
|
||||
//| :return: the data read
|
||||
//| :rtype: int
|
||||
//| Even if the underlying analog to digital converter (ADC) is lower
|
||||
//| resolution, the value is 16-bit.
|
||||
//|
|
||||
STATIC mp_obj_t analogio_analogin_obj_get_value(mp_obj_t self_in) {
|
||||
analogio_analogin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -130,10 +127,8 @@ const mp_obj_property_t analogio_analogin_value_obj = {
|
||||
|
||||
//| .. attribute:: reference_voltage
|
||||
//|
|
||||
//| The maximum voltage measurable. Also known as the reference voltage.
|
||||
//|
|
||||
//| :return: the reference voltage
|
||||
//| :rtype: float
|
||||
//| The maximum voltage measurable (also known as the reference voltage) as a
|
||||
//| `float` in Volts.
|
||||
//|
|
||||
STATIC mp_obj_t analogio_analogin_obj_get_reference_voltage(mp_obj_t self_in) {
|
||||
analogio_analogin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -105,13 +105,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(analogio_analogout___exit___obj, 4, 4
|
||||
|
||||
//| .. attribute:: value
|
||||
//|
|
||||
//| The value on the analog pin. The value must be between 0 and 65535
|
||||
//| inclusive (16-bit). Even if the underlying digital to analog converter
|
||||
//| is lower resolution, the input must be scaled to be 16-bit.
|
||||
//|
|
||||
//| :return: the last value written
|
||||
//| :rtype: int
|
||||
//| The value on the analog pin between 0 and 65535 inclusive (16-bit). (write-only)
|
||||
//|
|
||||
//| Even if the underlying digital to analog converter (DAC) is lower
|
||||
//| resolution, the value is 16-bit.
|
||||
STATIC mp_obj_t analogio_analogout_obj_set_value(mp_obj_t self_in, mp_obj_t value) {
|
||||
analogio_analogout_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
raise_error_if_deinited(common_hal_analogio_analogout_deinited(self));
|
||||
|
@ -187,7 +187,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audioio_audioout_stop_obj, audioio_audioout_obj_stop);
|
||||
|
||||
//| .. attribute:: playing
|
||||
//|
|
||||
//| True when the audio sample is being output.
|
||||
//| True when the audio sample is being output. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t audioio_audioout_obj_get_playing(mp_obj_t self_in) {
|
||||
audioio_audioout_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -253,7 +253,10 @@ const mp_obj_property_t digitalio_digitalinout_value_obj = {
|
||||
|
||||
//| .. attribute:: drive_mode
|
||||
//|
|
||||
//| Get or set the pin drive mode.
|
||||
//| The pin drive mode. One of:
|
||||
//|
|
||||
//| - `digitalio.DriveMode.PUSH_PULL`
|
||||
//| - `digitalio.DriveMode.OPEN_DRAIN`
|
||||
//|
|
||||
STATIC mp_obj_t digitalio_digitalinout_obj_get_drive_mode(mp_obj_t self_in) {
|
||||
digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -295,10 +298,13 @@ const mp_obj_property_t digitalio_digitalio_drive_mode_obj = {
|
||||
|
||||
//| .. attribute:: pull
|
||||
//|
|
||||
//| Get or set the pin pull. Values may be `digitalio.Pull.UP`,
|
||||
//| `digitalio.Pull.DOWN` or ``None``.
|
||||
//| The pin pull direction. One of:
|
||||
//|
|
||||
//| :raises AttributeError: if the direction is ~`digitalio.Direction.OUTPUT`.
|
||||
//| - `digitalio.Pull.UP`
|
||||
//| - `digitalio.Pull.DOWN`
|
||||
//| - `None`
|
||||
//|
|
||||
//| :raises AttributeError: if `direction` is :py:data:`~digitalio.Direction.OUTPUT`.
|
||||
//|
|
||||
STATIC mp_obj_t digitalio_digitalinout_obj_get_pull(mp_obj_t self_in) {
|
||||
digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -50,13 +50,13 @@
|
||||
|
||||
//| .. class:: Processor()
|
||||
//|
|
||||
//| You cannot create an instance of `microcontroller.Processor`.
|
||||
//| Use `microcontroller.cpu` to access the sole instance available.
|
||||
//| You cannot create an instance of `microcontroller.Processor`.
|
||||
//| Use `microcontroller.cpu` to access the sole instance available.
|
||||
//|
|
||||
|
||||
//| .. attribute:: frequency
|
||||
//| .. attribute:: frequency
|
||||
//|
|
||||
//| Return the CPU operating frequency as an int, in Hz.
|
||||
//| The CPU operating frequency as an `int`, in Hertz. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t mcu_processor_get_frequency(mp_obj_t self) {
|
||||
return mp_obj_new_int_from_uint(common_hal_mcu_processor_get_frequency());
|
||||
@ -72,10 +72,11 @@ const mp_obj_property_t mcu_processor_frequency_obj = {
|
||||
},
|
||||
};
|
||||
|
||||
//| .. attribute:: temperature
|
||||
//| .. attribute:: temperature
|
||||
//|
|
||||
//| Return the on-chip temperature, in Celsius, as a float.
|
||||
//| If the temperature is not available, return `None`.
|
||||
//| The on-chip temperature, in Celsius, as a float. (read-only)
|
||||
//|
|
||||
//| Is `None` if the temperature is not available.
|
||||
//|
|
||||
STATIC mp_obj_t mcu_processor_get_temperature(mp_obj_t self) {
|
||||
float temperature = common_hal_mcu_processor_get_temperature();
|
||||
@ -92,10 +93,9 @@ const mp_obj_property_t mcu_processor_temperature_obj = {
|
||||
},
|
||||
};
|
||||
|
||||
//| .. attribute:: uid
|
||||
//| .. attribute:: uid
|
||||
//|
|
||||
//| Return the unique id (aka serial number) of the chip.
|
||||
//| Returns a bytearray object.
|
||||
//| The unique id (aka serial number) of the chip as a `bytearray`. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t mcu_processor_get_uid(mp_obj_t self) {
|
||||
uint8_t raw_id[COMMON_HAL_MCU_PROCESSOR_UID_LENGTH];
|
||||
|
@ -201,7 +201,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(pulseio_pulsein_popleft_obj, pulseio_pulsein_obj_pople
|
||||
|
||||
//| .. attribute:: maxlen
|
||||
//|
|
||||
//| Returns the maximum length of the PulseIn. When len() is equal to maxlen,
|
||||
//| The maximum length of the PulseIn. When len() is equal to maxlen,
|
||||
//| it is unclear which pulses are active and which are idle.
|
||||
//|
|
||||
STATIC mp_obj_t pulseio_pulsein_obj_get_maxlen(mp_obj_t self_in) {
|
||||
|
@ -107,11 +107,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(touchio_touchin___exit___obj, 4, 4, t
|
||||
|
||||
//| .. attribute:: value
|
||||
//|
|
||||
//| Whether the touch pad is being touched or not.
|
||||
//| True if `raw_value` > `threshold`.
|
||||
//| Whether the touch pad is being touched or not. (read-only)
|
||||
//|
|
||||
//| :return: True when touched, False otherwise.
|
||||
//| :rtype: bool
|
||||
//| True when `raw_value` > `threshold`.
|
||||
//|
|
||||
STATIC mp_obj_t touchio_touchin_obj_get_value(mp_obj_t self_in) {
|
||||
touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -130,10 +128,7 @@ const mp_obj_property_t touchio_touchin_value_obj = {
|
||||
|
||||
//| .. attribute:: raw_value
|
||||
//|
|
||||
//| The raw touch measurement. Not settable.
|
||||
//|
|
||||
//| :return: an integer >= 0
|
||||
//| :rtype: int
|
||||
//| The raw touch measurement as an `int`. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t touchio_touchin_obj_get_raw_value(mp_obj_t self_in) {
|
||||
touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -153,14 +148,12 @@ const mp_obj_property_t touchio_touchin_raw_value_obj = {
|
||||
|
||||
//| .. attribute:: threshold
|
||||
//|
|
||||
//| `value` will return True if `raw_value` is greater than than this threshold.
|
||||
//| Minimum `raw_value` needed to detect a touch (and for `value` to be `True`).
|
||||
//|
|
||||
//| When the **TouchIn** object is created, an initial `raw_value` is read from the pin,
|
||||
//| and then `threshold` is set to be 100 + that value.
|
||||
//|
|
||||
//| You can set the threshold to a different value to make the pin more or less sensitive.
|
||||
//|
|
||||
//| :return: an integer >= 0
|
||||
//| :rtype: int
|
||||
//| You can adjust `threshold` to make the pin more or less sensitive.
|
||||
//|
|
||||
STATIC mp_obj_t touchio_touchin_obj_get_threshold(mp_obj_t self_in) {
|
||||
touchio_touchin_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
@ -67,10 +67,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(usb_hid_device_send_report_obj, usb_hid_device_send_re
|
||||
|
||||
//| .. attribute:: usage_page
|
||||
//|
|
||||
//| The usage page of the device. Can be thought of a category.
|
||||
//|
|
||||
//| :return: the device's usage page
|
||||
//| :rtype: int
|
||||
//| The usage page of the device as an `int`. Can be thought of a category. (read-only)
|
||||
//|
|
||||
STATIC mp_obj_t usb_hid_device_obj_get_usage_page(mp_obj_t self_in) {
|
||||
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
@ -87,12 +84,10 @@ const mp_obj_property_t usb_hid_device_usage_page_obj = {
|
||||
|
||||
//| .. attribute:: usage
|
||||
//|
|
||||
//| The functionality of the device. For example Keyboard is 0x06 within the
|
||||
//| generic desktop usage page 0x01. Mouse is 0x02 within the same usage
|
||||
//| page.
|
||||
//| The functionality of the device as an int. (read-only)
|
||||
//|
|
||||
//| :return: the usage within the usage page
|
||||
//| :rtype: int
|
||||
//| For example, Keyboard is 0x06 within the generic desktop usage page 0x01.
|
||||
//| Mouse is 0x02 within the same usage page.
|
||||
//|
|
||||
STATIC mp_obj_t usb_hid_device_obj_get_usage(mp_obj_t self_in) {
|
||||
usb_hid_device_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
Loading…
Reference in New Issue
Block a user