Skip to content

Commit bdab4fa

Browse files
authored
Merge pull request #61 from DiamondLightSource/copier-pyright
Update copier and adopt pyright
2 parents 751233b + 97b3756 commit bdab4fa

21 files changed

+210
-108
lines changed

.copier-answers.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Changes here will be overwritten by Copier
2-
_commit: 2.1.0-40-g9e70b8b
2+
_commit: 2.3.0
33
_src_path: gh:DiamondLightSource/python-copier-template
44
author_email: [email protected]
55
author_name: Gary Yendell
6+
component_lifecycle: experimental
67
component_owner: user:mef65357
8+
component_type: library
79
description: Control system agnostic framework for building Device support in Python
810
that will work for both EPICS and Tango
911
distribution_name: fastcs
@@ -14,4 +16,4 @@ github_org: DiamondLightSource
1416
package_name: fastcs
1517
pypi: true
1618
repo_name: FastCS
17-
type_checker: mypy
19+
type_checker: pyright

.github/CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ It is recommended that developers use a [vscode devcontainer](https://code.visua
2424

2525
This project was created using the [Diamond Light Source Copier Template](https://github.com/DiamondLightSource/python-copier-template) for Python projects.
2626

27-
For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.1.0/how-to.html).
27+
For more information on common tasks like setting up a developer environment, running the tests, and setting a pre-commit hook, see the template's [How-to guides](https://diamondlightsource.github.io/python-copier-template/2.3.0/how-to.html).

.github/pages/make_switcher.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Make switcher.json to allow docs to switch between different versions."""
2+
13
import json
24
import logging
35
from argparse import ArgumentParser
@@ -6,6 +8,7 @@
68

79

810
def report_output(stdout: bytes, label: str) -> list[str]:
11+
"""Print and return something received frm stdout."""
912
ret = stdout.decode().strip().split("\n")
1013
print(f"{label}: {ret}")
1114
return ret
@@ -52,21 +55,20 @@ def get_versions(ref: str, add: str | None) -> list[str]:
5255
return versions
5356

5457

55-
def write_json(path: Path, repository: str, versions: str):
58+
def write_json(path: Path, repository: str, versions: list[str]):
59+
"""Write the JSON switcher to path."""
5660
org, repo_name = repository.split("/")
57-
pages_url = f"https://{org}.github.io"
58-
if repo_name != f"{org}.github.io":
59-
# Only add the repo name if it isn't the source for the org pages site
60-
pages_url += f"/{repo_name}"
6161
struct = [
62-
{"version": version, "url": f"{pages_url}/{version}/"} for version in versions
62+
{"version": version, "url": f"https://{org}.github.io/{repo_name}/{version}/"}
63+
for version in versions
6364
]
6465
text = json.dumps(struct, indent=2)
6566
print(f"JSON switcher:\n{text}")
6667
path.write_text(text, encoding="utf-8")
6768

6869

6970
def main(args=None):
71+
"""Parse args and write switcher."""
7072
parser = ArgumentParser(
7173
description="Make a versions.json file from gh-pages directories"
7274
)

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cov.xml
5555

5656
# Sphinx documentation
5757
docs/_build/
58+
docs/_api
5859

5960
# PyBuilder
6061
target/

catalog-info.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ metadata:
55
title: FastCS
66
description: Control system agnostic framework for building Device support in Python that will work for both EPICS and Tango
77
spec:
8-
type: documentation
8+
type: library
99
lifecycle: experimental
1010
owner: user:mef65357

docs/_api.rst

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
:orphan:
2+
3+
..
4+
This page is not included in the TOC tree, but must exist so that the
5+
autosummary pages are generated for fastcs and all its
6+
subpackages
7+
8+
API
9+
===
10+
11+
.. autosummary::
12+
:toctree: _api
13+
:template: custom-module-template.rst
14+
:recursive:
15+
16+
fastcs
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{{ ('``' + fullname + '``') | underline }}
2+
3+
{%- set filtered_members = [] %}
4+
{%- for item in members %}
5+
{%- if item in functions + classes + exceptions + attributes %}
6+
{% set _ = filtered_members.append(item) %}
7+
{%- endif %}
8+
{%- endfor %}
9+
10+
.. automodule:: {{ fullname }}
11+
:members:
12+
13+
{% block modules %}
14+
{% if modules %}
15+
.. rubric:: Submodules
16+
17+
.. autosummary::
18+
:toctree:
19+
:template: custom-module-template.rst
20+
:recursive:
21+
{% for item in modules %}
22+
{{ item }}
23+
{%- endfor %}
24+
{% endif %}
25+
{% endblock %}
26+
27+
{% block members %}
28+
{% if filtered_members %}
29+
.. rubric:: Members
30+
31+
.. autosummary::
32+
:nosignatures:
33+
{% for item in filtered_members %}
34+
{{ item }}
35+
{%- endfor %}
36+
{% endif %}
37+
{% endblock %}

docs/conf.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
# Configuration file for the Sphinx documentation builder.
2-
#
3-
# This file only contains a selection of the most common options. For a full
4-
# list see the documentation:
5-
# https://www.sphinx-doc.org/en/master/usage/configuration.html
1+
"""Configuration file for the Sphinx documentation builder.
2+
3+
This file only contains a selection of the most common options. For a full
4+
list see the documentation:
5+
https://www.sphinx-doc.org/en/master/usage/configuration.html
6+
"""
67

78
import sys
89
from pathlib import Path
@@ -32,6 +33,8 @@
3233
extensions = [
3334
# Use this for generating API docs
3435
"sphinx.ext.autodoc",
36+
# and making summary tables at the top of API docs
37+
"sphinx.ext.autosummary",
3538
# This can parse google style docstrings
3639
"sphinx.ext.napoleon",
3740
# For linking to external sphinx documentation
@@ -83,6 +86,12 @@
8386
# Don't inherit docstrings from baseclasses
8487
autodoc_inherit_docstrings = False
8588

89+
# Document only what is in __all__
90+
autosummary_ignore_module_all = False
91+
92+
# Add any paths that contain templates here, relative to this directory.
93+
templates_path = ["_templates"]
94+
8695
# Output graphviz directive produced images in a scalable format
8796
graphviz_output_format = "svg"
8897

docs/reference.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Technical reference material including APIs and release notes.
66
:maxdepth: 1
77
:glob:
88
9-
reference/*
9+
API <_api/fastcs>
1010
genindex
1111
Release Notes <https://github.com/DiamondLightSource/FastCS/releases>
1212
```

docs/reference/api.md

-17
This file was deleted.

pyproject.toml

+7-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ dependencies = [
1414
"aioserial",
1515
"numpy",
1616
"pydantic",
17-
"pvi~=0.9.0",
17+
"pvi~=0.10.0",
1818
"pytango",
1919
"softioc",
2020
]
@@ -26,11 +26,11 @@ requires-python = ">=3.11"
2626
[project.optional-dependencies]
2727
dev = [
2828
"copier",
29-
"mypy",
3029
"myst-parser",
3130
"pipdeptree",
3231
"pre-commit",
3332
"pydata-sphinx-theme>=0.12",
33+
"pyright",
3434
"pytest",
3535
"pytest-cov",
3636
"pytest-mock",
@@ -59,8 +59,9 @@ name = "Martin Gaughran"
5959
[tool.setuptools_scm]
6060
version_file = "src/fastcs/_version.py"
6161

62-
[tool.mypy]
63-
ignore_missing_imports = true # Ignore missing stubs in imported modules
62+
[tool.pyright]
63+
typeCheckingMode = "standard"
64+
reportMissingImports = false # Ignore missing stubs in imported modules
6465

6566
[tool.pytest.ini_options]
6667
# Run pytest with all our checkers, and don't spam us with massive tracebacks on error
@@ -93,12 +94,12 @@ passenv = *
9394
allowlist_externals =
9495
pytest
9596
pre-commit
96-
mypy
97+
pyright
9798
sphinx-build
9899
sphinx-autobuild
99100
commands =
100101
pre-commit: pre-commit run --all-files --show-diff-on-failure {posargs}
101-
type-checking: mypy src tests {posargs}
102+
type-checking: pyright src tests {posargs}
102103
tests: pytest --cov=fastcs --cov-report term --cov-report xml:cov.xml {posargs}
103104
docs: sphinx-{posargs:build -EW --keep-going} -T docs build/html
104105
"""

src/fastcs/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
"""Top level API.
2+
3+
.. data:: __version__
4+
:type: str
5+
6+
Version number as calculated by https://github.com/pypa/setuptools_scm
7+
"""
8+
19
from ._version import __version__
210

311
__all__ = ["__version__"]

src/fastcs/__main__.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
"""Interface for ``python -m fastcs``."""
2+
13
from argparse import ArgumentParser
4+
from collections.abc import Sequence
25

36
from . import __version__
47

58
__all__ = ["main"]
69

710

8-
def main(args=None):
11+
def main(args: Sequence[str] | None = None) -> None:
12+
"""Argument parser for the CLI."""
913
parser = ArgumentParser()
10-
parser.add_argument("-v", "--version", action="version", version=__version__)
11-
args = parser.parse_args(args)
14+
parser.add_argument(
15+
"-v",
16+
"--version",
17+
action="version",
18+
version=__version__,
19+
)
20+
parser.parse_args(args)
1221

1322

14-
# test with: python -m fastcs
1523
if __name__ == "__main__":
1624
main()

src/fastcs/backends/epics/gui.py

+11-9
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
LED,
88
ButtonPanel,
99
ComboBox,
10-
Component,
10+
ComponentUnion,
1111
Device,
1212
Grid,
1313
Group,
14-
ReadWidget,
14+
ReadWidgetUnion,
1515
SignalR,
1616
SignalRW,
1717
SignalW,
@@ -22,7 +22,7 @@
2222
TextWrite,
2323
ToggleButton,
2424
Tree,
25-
WriteWidget,
25+
WriteWidgetUnion,
2626
)
2727
from pydantic import ValidationError
2828

@@ -56,7 +56,7 @@ def _get_pv(self, attr_path: list[str], name: str):
5656
return f"{attr_prefix}:{name.title().replace('_', '')}"
5757

5858
@staticmethod
59-
def _get_read_widget(attribute: AttrR) -> ReadWidget:
59+
def _get_read_widget(attribute: AttrR) -> ReadWidgetUnion:
6060
match attribute.datatype:
6161
case Bool():
6262
return LED()
@@ -68,7 +68,7 @@ def _get_read_widget(attribute: AttrR) -> ReadWidget:
6868
raise FastCSException(f"Unsupported type {type(datatype)}: {datatype}")
6969

7070
@staticmethod
71-
def _get_write_widget(attribute: AttrW) -> WriteWidget:
71+
def _get_write_widget(attribute: AttrW) -> WriteWidgetUnion:
7272
match attribute.allowed_values:
7373
case allowed_values if allowed_values is not None:
7474
return ComboBox(choices=allowed_values)
@@ -87,7 +87,7 @@ def _get_write_widget(attribute: AttrW) -> WriteWidget:
8787

8888
def _get_attribute_component(
8989
self, attr_path: list[str], name: str, attribute: Attribute
90-
):
90+
) -> SignalR | SignalW | SignalRW:
9191
pv = self._get_pv(attr_path, name)
9292
name = name.title().replace("_", "")
9393

@@ -108,6 +108,8 @@ def _get_attribute_component(
108108
case AttrW():
109109
write_widget = self._get_write_widget(attribute)
110110
return SignalW(name=name, write_pv=pv, write_widget=write_widget)
111+
case _:
112+
raise FastCSException(f"Unsupported attribute type: {type(attribute)}")
111113

112114
def _get_command_component(self, attr_path: list[str], name: str):
113115
pv = self._get_pv(attr_path, name)
@@ -136,8 +138,8 @@ def create_gui(self, options: EpicsGUIOptions | None = None) -> None:
136138
formatter = DLSFormatter()
137139
formatter.format(device, options.output_path)
138140

139-
def extract_mapping_components(self, mapping: SingleMapping) -> list[Component]:
140-
components: Tree[Component] = []
141+
def extract_mapping_components(self, mapping: SingleMapping) -> Tree:
142+
components: Tree = []
141143
attr_path = mapping.controller.path
142144

143145
for name, sub_controller in mapping.controller.get_sub_controllers().items():
@@ -151,7 +153,7 @@ def extract_mapping_components(self, mapping: SingleMapping) -> list[Component]:
151153
)
152154
)
153155

154-
groups: dict[str, list[Component]] = {}
156+
groups: dict[str, list[ComponentUnion]] = {}
155157
for attr_name, attribute in mapping.attributes.items():
156158
try:
157159
signal = self._get_attribute_component(

0 commit comments

Comments
 (0)