Skip to content

Commit a484bf2

Browse files
authored
begin docs (#1)
1 parent ec30ca2 commit a484bf2

20 files changed

+359
-27
lines changed

.readthedocs.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Read the Docs configuration file
2+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3+
4+
# Required
5+
version: 2
6+
7+
# Set the OS, Python version and other tools you might need
8+
build:
9+
os: ubuntu-22.04
10+
tools:
11+
python: "3.12"
12+
# You can also specify other tool versions:
13+
# nodejs: "19"
14+
# rust: "1.64"
15+
# golang: "1.19"
16+
17+
# Build documentation in the "docs/" directory with Sphinx
18+
sphinx:
19+
configuration: docs/conf.py
20+
21+
# Optionally build your docs in additional formats such as PDF and ePub
22+
# formats:
23+
# - pdf
24+
# - epub
25+
26+
# Optional but recommended, declare the Python requirements required
27+
# to build your documentation
28+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
29+
python:
30+
install:
31+
- requirements: docs/requirements.txt
32+
- method: pip
33+
path: '.'

README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
.. |rtd-badge| image:: https://readthedocs.org/projects/circuitpython-mocks/badge/?version=latest
2+
:target: https://circuitpython-mocks.readthedocs.io/en/latest/
3+
:alt: Documentation Status
4+
5+
|rtd-badge|
16

27
Read The Docs
38
=============

circuitpython_mocks/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,13 @@
44

55
@pytest.fixture(autouse=True)
66
def monkey_patch_sys_paths(monkeypatch: pytest.MonkeyPatch):
7+
"""A pytest fixture that monkey patches the Python runtime's import paths, such
8+
that this package's mock modules can be imported first (instead from the
9+
adafruit-blinka package).
10+
11+
.. important::
12+
13+
This fixture is automatically used once imported into the test module.
14+
"""
715
root_pkg = Path(__file__).parent
816
monkeypatch.syspath_prepend(str(root_pkg))

circuitpython_mocks/_mixins.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@ def unlock(self):
4040

4141

4242
class Expecting:
43+
"""A base class for the mock classes used to assert expected behaviors."""
4344
def __init__(self, **kwargs) -> None:
45+
#: A double ended queue used to assert expected behavior
4446
self.expectations: deque[Read | Write | Transfer | SetState | GetState] = (
4547
deque()
4648
)
4749
super().__init__(**kwargs)
4850

4951
def done(self):
52+
"""A function that asserts all `expectations` have been used.
53+
This is automatically called from the destructor."""
5054
assert not self.expectations, (
5155
"Some expectations were unused:\n "
5256
+ "\n ".join([repr(x) for x in self.expectations])

circuitpython_mocks/board.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
"""Mock pins"""
1+
"""A module that hosts mock pins."""
22

33
class Pin:
4+
"""A dummy type for GPIO pins."""
45
pass
56

67

circuitpython_mocks/busio/__init__.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
1+
"""A module to mock the data bus transactions."""
2+
13
from enum import Enum, auto
24
import sys
35
from typing import List
46

57
import circuitpython_typing
68

79
from circuitpython_mocks.busio.operations import (
8-
Read,
9-
Write,
10-
Transfer,
10+
UARTRead,
11+
UARTWrite,
1112
I2CRead,
1213
I2CWrite,
1314
I2CTransfer,
15+
SPIRead,
16+
SPIWrite,
17+
SPITransfer,
1418
)
1519
from circuitpython_mocks._mixins import Expecting, Lockable
1620
from circuitpython_mocks.board import Pin
1721

1822

1923
class I2C(Expecting, Lockable):
24+
"""A mock of `busio.I2C` class."""
25+
2026
def __init__(
2127
self,
2228
scl: Pin,
@@ -28,6 +34,8 @@ def __init__(
2834
super().__init__()
2935

3036
def scan(self) -> List[int]:
37+
"""Returns an empty list.
38+
Use :py:meth:`pytest.MonkeyPatch.setattr()` to change this output."""
3139
return []
3240

3341
def readfrom_into(
@@ -38,6 +46,9 @@ def readfrom_into(
3846
start: int = 0,
3947
end: int = sys.maxsize,
4048
) -> None:
49+
"""A mock imitation of :external:py:meth:`busio.I2C.readfrom_into()`.
50+
This function checks against `I2CRead`
51+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
4152
assert self.expectations, "no expectation found for I2C.readfrom_into()"
4253
op = self.expectations.popleft()
4354
assert isinstance(op, I2CRead), f"Read operation expected, found {repr(op)}"
@@ -53,6 +64,9 @@ def writeto(
5364
start: int = 0,
5465
end: int = sys.maxsize,
5566
) -> None:
67+
"""A mock imitation of :external:py:meth:`busio.I2C.writeto()`.
68+
This function checks against `I2CWrite`
69+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
5670
assert self.expectations, "no expectation found for I2C.writeto()"
5771
op = self.expectations.popleft()
5872
assert isinstance(op, I2CWrite), f"Read operation expected, found {repr(op)}"
@@ -70,6 +84,9 @@ def writeto_then_readfrom(
7084
in_start: int = 0,
7185
in_end: int = sys.maxsize,
7286
) -> None:
87+
"""A mock imitation of :external:py:meth:`busio.I2C.writeto_then_readfrom()`.
88+
This function checks against `I2CTransfer`
89+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
7390
assert self.expectations, "no expectation found for I2C.writeto_then_readfrom()"
7491
op = self.expectations.popleft()
7592
assert isinstance(
@@ -89,6 +106,7 @@ def __init__(
89106
MISO: Pin | None = None,
90107
half_duplex: bool = False,
91108
):
109+
"""A class to mock :external:py:class:`busio.SPI`."""
92110
super().__init__()
93111
self._frequency = 1000000
94112

@@ -100,10 +118,12 @@ def configure(
100118
phase: int = 0,
101119
bits: int = 8,
102120
) -> None:
121+
"""A dummy function to mock :external:py:meth:`busio.SPI.configure()`"""
103122
self._frequency = baudrate
104123

105124
@property
106125
def frequency(self) -> int:
126+
"""Returns the value passed to ``baudrate`` parameter of `configure()`."""
107127
return self._frequency
108128

109129
def write(
@@ -113,9 +133,12 @@ def write(
113133
start: int = 0,
114134
end: int = sys.maxsize,
115135
) -> None:
136+
"""A function that mocks :external:py:meth:`busio.SPI.write()`.
137+
This function checks against `SPIWrite`
138+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
116139
assert self.expectations, "no expectation found for SPI.write()"
117140
op = self.expectations.popleft()
118-
assert isinstance(op, Write), f"Read operation expected, found {repr(op)}"
141+
assert isinstance(op, SPIWrite), f"Read operation expected, found {repr(op)}"
119142
op.assert_expected(buffer, start, end)
120143

121144
def readinto(
@@ -126,9 +149,12 @@ def readinto(
126149
end: int = sys.maxsize,
127150
write_value: int = 0,
128151
) -> None:
152+
"""A function that mocks :external:py:meth:`busio.SPI.readinto()`.
153+
This function checks against `SPIRead`
154+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
129155
assert self.expectations, "no expectation found for SPI.readinto()"
130156
op = self.expectations.popleft()
131-
assert isinstance(op, Read), f"Read operation expected, found {repr(op)}"
157+
assert isinstance(op, SPIRead), f"Read operation expected, found {repr(op)}"
132158
op.assert_response(buffer, start, end)
133159

134160
def write_readinto(
@@ -141,20 +167,23 @@ def write_readinto(
141167
in_start: int = 0,
142168
in_end: int = sys.maxsize,
143169
) -> None:
170+
"""A function that mocks :external:py:meth:`busio.SPI.write_readinto()`.
171+
This function checks against `SPITransfer`
172+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
144173
assert self.expectations, "no expectation found for I2C.writeto_then_readfrom()"
145174
op = self.expectations.popleft()
146175
assert isinstance(
147-
op, Transfer
176+
op, SPITransfer
148177
), f"Transfer operation expected, found {repr(op)}"
149178
op.assert_transaction(
150179
out_buffer, in_buffer, out_start, out_end, in_start, in_end
151180
)
152181

153182

154183
class UART(Expecting, Lockable):
155-
class Parity(Enum):
156-
"""Parity Enumeration"""
184+
"""A class that mocks :external:py:class:`busio.UART`."""
157185

186+
class Parity(Enum):
158187
ODD = auto()
159188
EVEN = auto()
160189

@@ -177,35 +206,47 @@ def __init__(
177206
super().__init__()
178207

179208
def read(self, nbytes: int | None = None) -> bytes | None:
209+
"""A function that mocks :external:py:meth:`busio.UART.read()`.
210+
This function checks against `UARTRead`
211+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
180212
assert self.expectations, "no expectation found for UART.read()"
181213
op = self.expectations.popleft()
182-
assert isinstance(op, Read), f"Read operation expected, found {repr(op)}"
214+
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
183215
length = nbytes or len(op.response)
184216
buffer = bytearray(length)
185217
op.assert_response(buffer, 0, length)
186218
return bytes(buffer)
187219

188220
def readinto(self, buf: circuitpython_typing.WriteableBuffer) -> int | None:
221+
"""A function that mocks :external:py:meth:`busio.UART.readinto()`.
222+
This function checks against `UARTRead`
223+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
189224
assert self.expectations, "no expectation found for UART.readinto()"
190225
op = self.expectations.popleft()
191-
assert isinstance(op, Read), f"Read operation expected, found {repr(op)}"
226+
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
192227
len_buf = len(op.response)
193228
op.assert_response(buf, 0, len_buf)
194229
return len_buf
195230

196231
def readline(self) -> bytes:
232+
"""A function that mocks :external:py:meth:`busio.UART.readline()`.
233+
This function checks against `UARTRead`
234+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
197235
assert self.expectations, "no expectation found for UART.readline()"
198236
op = self.expectations.popleft()
199-
assert isinstance(op, Read), f"Read operation expected, found {repr(op)}"
237+
assert isinstance(op, UARTRead), f"Read operation expected, found {repr(op)}"
200238
len_buf = len(op.response)
201239
buf = bytearray(len_buf)
202240
op.assert_response(buf, 0, len_buf)
203241
return bytes(buf)
204242

205243
def write(self, buf: circuitpython_typing.ReadableBuffer) -> int | None:
244+
"""A function that mocks :external:py:meth:`busio.UART.write()`.
245+
This function checks against `UARTWrite`
246+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`"""
206247
assert self.expectations, "no expectation found for UART.write()"
207248
op = self.expectations.popleft()
208-
assert isinstance(op, Write), f"Read operation expected, found {repr(op)}"
249+
assert isinstance(op, UARTWrite), f"Read operation expected, found {repr(op)}"
209250
len_buf = len(op.expected)
210251
op.assert_expected(buf, 0, len_buf)
211252
return len(buf) or None

circuitpython_mocks/busio/operations.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44

55
class Write:
6+
"""A class to identify a write operation over a data bus."""
7+
68
def __init__(self, expected: bytearray, **kwargs) -> None:
79
self.expected = expected
810
super().__init__(**kwargs)
@@ -25,6 +27,8 @@ def assert_expected(
2527

2628

2729
class Read:
30+
"""A class to identify a read operation over a data bus."""
31+
2832
def __init__(self, response: bytearray, **kwargs) -> None:
2933
self.response = response
3034
super().__init__(**kwargs)
@@ -48,6 +52,8 @@ def assert_response(
4852

4953

5054
class Transfer(Read, Write):
55+
"""A class to identify a read/write (transfer) operation over a data bus."""
56+
5157
def __init__(self, expected: bytearray, response: bytearray, **kwargs) -> None:
5258
super().__init__(response=response, expected=expected, **kwargs)
5359

@@ -93,6 +99,8 @@ def assert_address(self, address: int):
9399

94100

95101
class I2CWrite(Write, _I2CAddress):
102+
"""A class to identify a write operation over a I2C bus."""
103+
96104
def __init__(self, address: int, expected: bytearray) -> None:
97105
super().__init__(expected=expected, address=address)
98106

@@ -103,6 +111,8 @@ def __repr__(self) -> str:
103111

104112

105113
class I2CRead(Read, _I2CAddress):
114+
"""A class to identify a read operation over a I2C bus."""
115+
106116
def __init__(self, address: int, response: bytearray) -> None:
107117
super().__init__(response=response, address=address)
108118

@@ -113,6 +123,8 @@ def __repr__(self) -> str:
113123

114124

115125
class I2CTransfer(Transfer, _I2CAddress):
126+
"""A class to identify a write operation over a I2C bus."""
127+
116128
def __init__(self, address: int, expected: bytearray, response: bytearray) -> None:
117129
super().__init__(expected=expected, response=response, address=address)
118130

@@ -126,14 +138,20 @@ def __repr__(self) -> str:
126138

127139

128140
class SPIRead(Read):
141+
"""A class to identify a read operation over a SPI bus."""
142+
129143
pass
130144

131145

132146
class SPIWrite(Write):
147+
"""A class to identify a write operation over a SPI bus."""
148+
133149
pass
134150

135151

136152
class SPITransfer(Transfer):
153+
"""A class to identify a read/write (transfer) operation over a SPI bus."""
154+
137155
def assert_transaction(
138156
self,
139157
out_buffer: cir_py_types.WriteableBuffer,
@@ -150,3 +168,15 @@ def assert_transaction(
150168
super().assert_transaction(
151169
out_buffer, in_buffer, out_start, out_end, in_start, in_end
152170
)
171+
172+
173+
class UARTRead(Read):
174+
"""A class to identify a read operation over a UART bus."""
175+
176+
pass
177+
178+
179+
class UARTWrite(Write):
180+
"""A class to identify a write operation over a UART bus."""
181+
182+
pass

circuitpython_mocks/digitalio/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Pull(Enum):
2727

2828

2929
class DigitalInOut(Expecting, ContextManaged):
30+
"""A class that mocks :external:py:class:digitalio.DigitalInOut`"""
3031
def __init__(self, pin: Pin, **kwargs):
3132
super().__init__(**kwargs)
3233
self._pin = pin
@@ -65,7 +66,9 @@ def direction(self, value):
6566

6667
@property
6768
def value(self):
68-
"""The Digital Pin Value"""
69+
"""The Digital Pin Value.
70+
This property will check against `SetState` and `GetState`
71+
:py:attr:`~circuitpython_mocks._mixins.Expecting.expectations`."""
6972
assert self.expectations, "No expectations found for DigitalInOut.value.getter"
7073
op = self.expectations.popleft()
7174
assert isinstance(

0 commit comments

Comments
 (0)