Skip to content

Commit 6fab8eb

Browse files
committed
feat: add network module documentation and related tests.
Fixes: #672 Signed-off-by: Jos Verlinde <[email protected]>
1 parent 2483727 commit 6fab8eb

File tree

5 files changed

+350
-21
lines changed

5 files changed

+350
-21
lines changed

src/stubber/rst/lookup.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,12 @@ class Fix:
445445
"(ip, subnet, gateway, dns):Optional[Any]=None",
446446
"configtuple: Optional[Tuple]",
447447
),
448+
# network.ipconfig
449+
Fix(
450+
"param=value",
451+
"param:Optional[str]=None",
452+
name="ipconfig",
453+
),
448454
# framebuffer
449455
# unresolvable parameter defaults # FrameBuffer: def __init__
450456
Fix(

src/stubber/rst/reader.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,19 @@
6969

7070
from mpflash.logger import log
7171
from mpflash.versions import V_PREVIEW
72-
from stubber.rst import (CHILD_PARENT_CLASS, MODULE_GLUE, PARAM_FIXES,
73-
PARAM_RE_FIXES, RST_DOC_FIXES, TYPING_IMPORT,
74-
ClassSourceDict, FunctionSourceDict, ModuleSourceDict,
75-
return_type_from_context)
72+
73+
from stubber.rst import (
74+
CHILD_PARENT_CLASS,
75+
MODULE_GLUE,
76+
PARAM_FIXES,
77+
PARAM_RE_FIXES,
78+
RST_DOC_FIXES,
79+
TYPING_IMPORT,
80+
ClassSourceDict,
81+
FunctionSourceDict,
82+
ModuleSourceDict,
83+
return_type_from_context,
84+
)
7685
from stubber.rst.lookup import Fix
7786
from stubber.utils.config import CONFIG
7887

@@ -496,8 +505,8 @@ def parse_toc(self):
496505
self.read_file(file_path)
497506
self.parse()
498507
# reset this file to done
499-
self.rst_text = []
500-
self.line_no = 1
508+
# self.rst_text = []
509+
# self.line_no = 1
501510

502511
def parse_module(self):
503512
"parse a module tag and set the module's docstring"

src/stubber/stubs_from_docs.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,23 +78,11 @@ def get_rst_sources(rst_path: Path, pattern: str) -> List[Path]:
7878
def make_docstubs(
7979
dst_path: Path, v_tag: str, release: str, suffix: str, files: List[Path], clean_rst: bool
8080
):
81-
"""Create the docstubs"""
81+
"""Create docstubs from the list of rst files"""
8282

8383
for file in files:
84-
reader = RSTWriter(v_tag)
85-
reader.clean_rst = clean_rst
86-
reader.source_release = release
87-
log.debug(f"Reading: {file}")
88-
reader.read_file(file)
89-
reader.parse()
90-
# Destination = "module.__init__.pyi"
91-
if "." in file.stem:
92-
target = dst_path / f"{(file.stem).replace('.', '/')}{suffix}"
93-
else:
94-
target = dst_path / file.stem / f"__init__{suffix}"
95-
# fname = (dst_path / file.name).with_suffix(suffix)
96-
reader.write_file(target)
97-
del reader
84+
make_docstub(file, dst_path, v_tag, release, suffix, clean_rst)
85+
9886
for name in U_MODULES:
9987
# create a file "umodule.pyi" for each module
10088
# and add a line : from module import *
@@ -107,3 +95,28 @@ def make_docstubs(
10795
f.write("# Allow the use of micro-module notation \n\n")
10896
f.write(f"from {name} import * # type: ignore\n")
10997
f.flush()
98+
99+
100+
def make_docstub(
101+
file: Path,
102+
dst_path: Path,
103+
v_tag: str,
104+
release: str,
105+
suffix: str,
106+
clean_rst: bool,
107+
):
108+
"""Create a docstub from a single rst file"""
109+
reader = RSTWriter(v_tag)
110+
reader.clean_rst = clean_rst
111+
reader.source_release = release
112+
log.debug(f"Reading: {file}")
113+
reader.read_file(file)
114+
reader.parse()
115+
116+
if "." in file.stem:
117+
target = dst_path / f"{(file.stem).replace('.', '/')}{suffix}"
118+
else:
119+
target = dst_path / file.stem / f"__init__{suffix}"
120+
121+
reader.write_file(target)
122+
del reader

tests/rst/data/network.rst

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
****************************************
2+
:mod:`network` --- network configuration
3+
****************************************
4+
5+
.. module:: network
6+
:synopsis: network configuration
7+
8+
This module provides network drivers and routing configuration. To use this
9+
module, a MicroPython variant/build with network capabilities must be installed.
10+
Network drivers for specific hardware are available within this module and are
11+
used to configure hardware network interface(s). Network services provided
12+
by configured interfaces are then available for use via the :mod:`socket`
13+
module.
14+
15+
For example::
16+
17+
# connect/ show IP config a specific network interface
18+
# see below for examples of specific drivers
19+
import network
20+
import time
21+
nic = network.Driver(...)
22+
if not nic.isconnected():
23+
nic.connect()
24+
print("Waiting for connection...")
25+
while not nic.isconnected():
26+
time.sleep(1)
27+
print(nic.ipconfig("addr4"))
28+
29+
# now use socket as usual
30+
import socket
31+
addr = socket.getaddrinfo('micropython.org', 80)[0][-1]
32+
s = socket.socket()
33+
s.connect(addr)
34+
s.send(b'GET / HTTP/1.1\r\nHost: micropython.org\r\n\r\n')
35+
data = s.recv(1000)
36+
s.close()
37+
38+
Common network adapter interface
39+
================================
40+
41+
This section describes an (implied) abstract base class for all network
42+
interface classes implemented by :term:`MicroPython ports <MicroPython port>`
43+
for different hardware. This means that MicroPython does not actually
44+
provide ``AbstractNIC`` class, but any actual NIC class, as described
45+
in the following sections, implements methods as described here.
46+
47+
.. class:: AbstractNIC(id=None, ...)
48+
49+
Instantiate a network interface object. Parameters are network interface
50+
dependent. If there are more than one interface of the same type, the first
51+
parameter should be `id`.
52+
53+
.. method:: AbstractNIC.active([is_active])
54+
55+
Activate ("up") or deactivate ("down") the network interface, if
56+
a boolean argument is passed. Otherwise, query current state if
57+
no argument is provided. Most other methods require an active
58+
interface (behaviour of calling them on inactive interface is
59+
undefined).
60+
61+
.. method:: AbstractNIC.connect([service_id, key=None, *, ...])
62+
63+
Connect the interface to a network. This method is optional, and
64+
available only for interfaces which are not "always connected".
65+
If no parameters are given, connect to the default (or the only)
66+
service. If a single parameter is given, it is the primary identifier
67+
of a service to connect to. It may be accompanied by a key
68+
(password) required to access said service. There can be further
69+
arbitrary keyword-only parameters, depending on the networking medium
70+
type and/or particular device. Parameters can be used to: a)
71+
specify alternative service identifier types; b) provide additional
72+
connection parameters. For various medium types, there are different
73+
sets of predefined/recommended parameters, among them:
74+
75+
* WiFi: *bssid* keyword to connect to a specific BSSID (MAC address)
76+
77+
.. method:: AbstractNIC.disconnect()
78+
79+
Disconnect from network.
80+
81+
.. method:: AbstractNIC.isconnected()
82+
83+
Returns ``True`` if connected to network, otherwise returns ``False``.
84+
85+
.. method:: AbstractNIC.scan(*, ...)
86+
87+
Scan for the available network services/connections. Returns a
88+
list of tuples with discovered service parameters. For various
89+
network media, there are different variants of predefined/
90+
recommended tuple formats, among them:
91+
92+
* WiFi: (ssid, bssid, channel, RSSI, security, hidden). There
93+
may be further fields, specific to a particular device.
94+
95+
The function may accept additional keyword arguments to filter scan
96+
results (e.g. scan for a particular service, on a particular channel,
97+
for services of a particular set, etc.), and to affect scan
98+
duration and other parameters. Where possible, parameter names
99+
should match those in connect().
100+
101+
.. method:: AbstractNIC.status([param])
102+
103+
Query dynamic status information of the interface. When called with no
104+
argument the return value describes the network link status. Otherwise
105+
*param* should be a string naming the particular status parameter to
106+
retrieve.
107+
108+
The return types and values are dependent on the network
109+
medium/technology. Some of the parameters that may be supported are:
110+
111+
* WiFi STA: use ``'rssi'`` to retrieve the RSSI of the AP signal
112+
* WiFi AP: use ``'stations'`` to retrieve a list of all the STAs
113+
connected to the AP. The list contains tuples of the form
114+
(MAC, RSSI).
115+
116+
.. method:: AbstractNIC.ipconfig('param')
117+
AbstractNIC.ipconfig(param=value, ...)
118+
119+
Get or set interface-specific IP-configuration interface parameters.
120+
Supported parameters are the following (availability of a particular
121+
parameter depends on the port and the specific network interface):
122+
123+
* ``dhcp4`` (``True/False``) obtain an IPv4 address, gateway and dns
124+
server via DHCP. This method does not block and wait for an address
125+
to be obtained. To check if an address was obtained, use the read-only
126+
property ``has_dhcp4``.
127+
* ``gw4`` Get/set the IPv4 default-gateway.
128+
* ``dhcp6`` (``True/False``) obtain a DNS server via stateless DHCPv6.
129+
Obtaining IP Addresses via DHCPv6 is currently not implemented.
130+
* ``autoconf6`` (``True/False``) obtain a stateless IPv6 address via
131+
the network prefix shared in router advertisements. To check if a
132+
stateless address was obtained, use the read-only
133+
property ``has_autoconf6``.
134+
* ``addr4`` (e.g. ``192.168.0.4/24``) obtain the current IPv4 address
135+
and network mask as ``(ip, subnet)``-tuple, regardless of how this
136+
address was obtained. This method can be used to set a static IPv4
137+
address either as ``(ip, subnet)``-tuple or in CIDR-notation.
138+
* ``addr6`` (e.g. ``fe80::1234:5678``) obtain a list of current IPv6
139+
addresses as ``(ip, state, preferred_lifetime, valid_lifetime)``-tuple.
140+
This include link-local, slaac and static addresses.
141+
``preferred_lifetime`` and ``valid_lifetime`` represent the remaining
142+
valid and preferred lifetime of each IPv6 address, in seconds.
143+
``state`` indicates the current state of the address:
144+
145+
* ``0x08`` - ``0x0f`` indicates the address is tentative, counting the
146+
number of probes sent.
147+
* ``0x10`` The address is deprecated (but still valid)
148+
* ``0x30`` The address is preferred (and valid)
149+
* ``0x40`` The address is duplicated and can not be used.
150+
151+
This method can be used to set a static IPv6
152+
address, by setting this parameter to the address, like ``fe80::1234:5678``.
153+
154+
.. method:: AbstractNIC.ifconfig([(ip, subnet, gateway, dns)])
155+
156+
.. note:: This function is deprecated, use `ipconfig()` instead.
157+
158+
Get/set IP-level network interface parameters: IP address, subnet mask,
159+
gateway and DNS server. When called with no arguments, this method returns
160+
a 4-tuple with the above information. To set the above values, pass a
161+
4-tuple with the required information. For example::
162+
163+
nic.ifconfig(('192.168.0.4', '255.255.255.0', '192.168.0.1', '8.8.8.8'))
164+
165+
.. method:: AbstractNIC.config('param')
166+
AbstractNIC.config(param=value, ...)
167+
168+
Get or set general network interface parameters. These methods allow to work
169+
with additional parameters beyond standard IP configuration (as dealt with by
170+
`ipconfig()`). These include network-specific and hardware-specific
171+
parameters. For setting parameters, the keyword argument
172+
syntax should be used, and multiple parameters can be set at once. For
173+
querying, a parameter name should be quoted as a string, and only one
174+
parameter can be queried at a time::
175+
176+
# Set WiFi access point name (formally known as SSID) and WiFi channel
177+
ap.config(ssid='My AP', channel=11)
178+
# Query params one by one
179+
print(ap.config('ssid'))
180+
print(ap.config('channel'))
181+
182+
Specific network class implementations
183+
======================================
184+
185+
The following concrete classes implement the AbstractNIC interface and
186+
provide a way to control networking interfaces of various kinds.
187+
188+
.. toctree::
189+
:maxdepth: 1
190+
191+
network.WLAN.rst
192+
network.WLANWiPy.rst
193+
network.WIZNET5K.rst
194+
network.LAN.rst
195+
network.PPP.rst
196+
197+
Network functions
198+
=================
199+
200+
The following are functions available in the network module.
201+
202+
.. function:: country([code])
203+
204+
Get or set the two-letter ISO 3166-1 Alpha-2 country code to be used for
205+
radio compliance.
206+
207+
If the *code* parameter is provided, the country will be set to this value.
208+
If the function is called without parameters, it returns the current
209+
country.
210+
211+
The default code ``"XX"`` represents the "worldwide" region.
212+
213+
.. function:: hostname([name])
214+
215+
Get or set the hostname that will identify this device on the network. It will
216+
be used by all interfaces.
217+
218+
This hostname is used for:
219+
* Sending to the DHCP server in the client request. (If using DHCP)
220+
* Broadcasting via mDNS. (If enabled)
221+
222+
If the *name* parameter is provided, the hostname will be set to this value.
223+
If the function is called without parameters, it returns the current
224+
hostname.
225+
226+
A change in hostname is typically only applied during connection. For DHCP
227+
this is because the hostname is part of the DHCP client request, and the
228+
implementation of mDNS in most ports only initialises the hostname once
229+
during connection. For this reason, you must set the hostname before
230+
activating/connecting your network interfaces.
231+
232+
The length of the hostname is limited to 32 characters.
233+
:term:`MicroPython ports <MicroPython port>` may choose to set a lower
234+
limit for memory reasons. If the given name does not fit, a `ValueError`
235+
is raised.
236+
237+
The default hostname is typically the name of the board.
238+
239+
.. function:: ipconfig('param')
240+
ipconfig(param=value, ...)
241+
242+
Get or set global IP-configuration parameters.
243+
Supported parameters are the following (availability of a particular
244+
parameter depends on the port and the specific network interface):
245+
246+
* ``dns`` Get/set DNS server. This method can support both, IPv4 and
247+
IPv6 addresses.
248+
* ``prefer`` (``4/6``) Specify which address type to return, if a domain
249+
name has both A and AAAA records. Note, that this does not clear the
250+
local DNS cache, so that any previously obtained addresses might not
251+
change.
252+
253+
.. function:: phy_mode([mode])
254+
255+
Get or set the PHY mode.
256+
257+
If the *mode* parameter is provided, the PHY mode will be set to this value.
258+
If the function is called without parameters, it returns the current PHY
259+
mode.
260+
261+
The possible modes are defined as constants:
262+
* ``MODE_11B`` -- IEEE 802.11b,
263+
* ``MODE_11G`` -- IEEE 802.11g,
264+
* ``MODE_11N`` -- IEEE 802.11n.
265+
266+
Availability: ESP8266.

0 commit comments

Comments
 (0)