Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
98ba041
Change imports to 'from y import x as x' for compatibility with pyrig…
jakkdl Apr 5, 2023
d66b030
Add pyright_verifytypes to test_exports.test_static_tool_sees_all_sym…
jakkdl Apr 7, 2023
98d9db6
Add check for type completeness not going down
jakkdl Apr 7, 2023
881f45b
trim json file, get closer to it working in CI
jakkdl Apr 7, 2023
133b222
fix failing CI
jakkdl Apr 7, 2023
bbb4c9b
.
jakkdl Apr 7, 2023
e0f68fc
remove paths from json, fix ci?
jakkdl Apr 10, 2023
2f6cfc3
fix symbols on windows&mac, fix formatting errors
jakkdl Apr 10, 2023
ffd1984
undo modifications to req files, import WaitForSingleObject as...
jakkdl Apr 10, 2023
57f564d
move files before merge
jakkdl May 25, 2023
2446507
Merge branch 'main' into merge_importx
jakkdl May 25, 2023
14cf061
update verify_types.json
jakkdl May 25, 2023
ee9bcb8
print json diff if modified
jakkdl May 25, 2023
8b01b68
update pyright version
jakkdl May 25, 2023
fe68911
fix windows 3.7 CI, remove version from json
jakkdl May 25, 2023
f18755d
add newline at end of json, another attempt at fixing 3.7@windows
jakkdl May 25, 2023
6a562d7
fix porcelain check for modified json, another attempt fixing 3.7@win
jakkdl May 25, 2023
9f4058a
coverage changes, fix 3.7@win
jakkdl May 25, 2023
78ee000
fix formatting + flake8 warnings
jakkdl May 25, 2023
0bb769c
properly exclude check script from coverage
jakkdl May 25, 2023
2e34089
Merge branch 'main' into import_x_as_x
jakkdl May 30, 2023
2c161b9
fix review comments, add full verify_types.json
jakkdl May 30, 2023
a78123a
remove path/line/col references in errors
jakkdl May 30, 2023
7e12b88
filter out symbols with no errors in json, use --outputjson in test_e…
jakkdl May 30, 2023
4560301
Merge branch 'master' into import_x_as_x
jakkdl May 31, 2023
0a65d5d
reexport constants from socket with 'import x as x' for pyright
jakkdl May 31, 2023
ddece80
update comment on `__version__`
jakkdl May 31, 2023
21b3f75
type: ignore[attr-defined] on the big import in socket
jakkdl May 31, 2023
968ec64
update verify_types since trio.socket re-exports are visible
jakkdl May 31, 2023
b018e76
remove --ignoreexternal
jakkdl May 31, 2023
811a4b8
reintroduce --ignoreexternal, prune symbols list heavily
jakkdl Jun 1, 2023
54f79ab
merge main
jakkdl Jun 6, 2023
bf1d995
update verify_types.json
jakkdl Jun 6, 2023
d007808
review fixes
jakkdl Jun 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ omit=
*/ipython_custom_exc.py
# Omit the generated files in trio/_core starting with _generated_
*/trio/_core/_generated_*
# script used to check type completeness that isn't run in tests
*/trio/_tests/check_type_completeness.py
# The test suite spawns subprocesses to test some stuff, so make sure
# this doesn't corrupt the coverage files
parallel=True
Expand Down
7 changes: 7 additions & 0 deletions check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ if git status --porcelain | grep -q "requirements.txt"; then
EXIT_STATUS=1
fi

python trio/_tests/check_type_completeness.py --overwrite-file || EXIT_STATUS=$?
Comment thread
A5rocks marked this conversation as resolved.
if git status --porcelain trio/_tests/verify_types.json | grep -q "M"; then
echo "Type completeness changed, please update!"
git diff trio/_tests/verify_types.json
EXIT_STATUS=1
fi

# Finally, leave a really clear warning of any issues and exit
if [ $EXIT_STATUS -ne 0 ]; then
cat <<EOF
Expand Down
4 changes: 4 additions & 0 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

set -ex -o pipefail

# disable warnings about pyright being out of date
# used in test_exports and in check.sh
export PYRIGHT_PYTHON_IGNORE_WARNINGS=1
Comment thread
jakkdl marked this conversation as resolved.

# Log some general info about the environment
uname -a
env | sort
Expand Down
1 change: 1 addition & 0 deletions test-requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pytest >= 5.0 # for faulthandler in core
pytest-cov >= 2.6.0
async_generator >= 1.9
pyright
# ipython 7.x is the last major version supporting Python 3.7
ipython < 7.35 # for the IPython traceback integration tests
pyOpenSSL >= 22.0.0 # for the ssl + DTLS tests
Expand Down
4 changes: 4 additions & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ mypy-extensions==1.0.0 ; implementation_name == "cpython"
# -r test-requirements.in
# black
# mypy
nodeenv==1.7.0
# via pyright
outcome==1.2.0
# via -r test-requirements.in
packaging==23.1
Expand Down Expand Up @@ -116,6 +118,8 @@ pyopenssl==23.2.0
# via -r test-requirements.in
pyproject-hooks==1.0.0
# via build
pyright==1.1.310
# via -r test-requirements.in
pytest==7.3.1
# via
# -r test-requirements.in
Expand Down
115 changes: 66 additions & 49 deletions trio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,87 +12,104 @@
#
# This file pulls together the friendly public API, by re-exporting the more
# innocuous bits of the _core API + the higher-level tools from trio/*.py.
#
# Uses `from x import y as y` for compatibility with `pyright --verifytypes` (#2625)

# pyright explicitly does not care about `__version__`
# see https://github.com/microsoft/pyright/blob/main/docs/typed-libraries.md#type-completeness
from ._version import __version__
Comment thread
A5rocks marked this conversation as resolved.

from ._core import (
TrioInternalError,
RunFinishedError,
WouldBlock,
Cancelled,
BusyResourceError,
ClosedResourceError,
run,
open_nursery,
CancelScope,
current_effective_deadline,
TASK_STATUS_IGNORED,
current_time,
BrokenResourceError,
EndOfChannel,
Nursery,
TrioInternalError as TrioInternalError,
RunFinishedError as RunFinishedError,
WouldBlock as WouldBlock,
Cancelled as Cancelled,
BusyResourceError as BusyResourceError,
ClosedResourceError as ClosedResourceError,
run as run,
open_nursery as open_nursery,
CancelScope as CancelScope,
current_effective_deadline as current_effective_deadline,
TASK_STATUS_IGNORED as TASK_STATUS_IGNORED,
current_time as current_time,
BrokenResourceError as BrokenResourceError,
EndOfChannel as EndOfChannel,
Nursery as Nursery,
)

from ._timeouts import (
move_on_at,
move_on_after,
sleep_forever,
sleep_until,
sleep,
fail_at,
fail_after,
TooSlowError,
move_on_at as move_on_at,
move_on_after as move_on_after,
sleep_forever as sleep_forever,
sleep_until as sleep_until,
sleep as sleep,
fail_at as fail_at,
fail_after as fail_after,
TooSlowError as TooSlowError,
)

from ._sync import (
Event,
CapacityLimiter,
Semaphore,
Lock,
StrictFIFOLock,
Condition,
Event as Event,
CapacityLimiter as CapacityLimiter,
Semaphore as Semaphore,
Lock as Lock,
StrictFIFOLock as StrictFIFOLock,
Condition as Condition,
)

from ._highlevel_generic import aclose_forcefully, StapledStream
from ._highlevel_generic import (
aclose_forcefully as aclose_forcefully,
StapledStream as StapledStream,
)

from ._channel import (
open_memory_channel,
MemorySendChannel,
MemoryReceiveChannel,
open_memory_channel as open_memory_channel,
MemorySendChannel as MemorySendChannel,
MemoryReceiveChannel as MemoryReceiveChannel,
)

from ._signals import open_signal_receiver
from ._signals import open_signal_receiver as open_signal_receiver

from ._highlevel_socket import SocketStream, SocketListener
from ._highlevel_socket import (
SocketStream as SocketStream,
SocketListener as SocketListener,
)

from ._file_io import open_file, wrap_file
from ._file_io import open_file as open_file, wrap_file as wrap_file

from ._path import Path
from ._path import Path as Path

from ._subprocess import Process, run_process
from ._subprocess import Process as Process, run_process as run_process

from ._ssl import SSLStream, SSLListener, NeedHandshakeError
from ._ssl import (
SSLStream as SSLStream,
SSLListener as SSLListener,
NeedHandshakeError as NeedHandshakeError,
)

from ._dtls import DTLSEndpoint, DTLSChannel
from ._dtls import DTLSEndpoint as DTLSEndpoint, DTLSChannel as DTLSChannel

from ._highlevel_serve_listeners import serve_listeners
from ._highlevel_serve_listeners import serve_listeners as serve_listeners

from ._highlevel_open_tcp_stream import open_tcp_stream
from ._highlevel_open_tcp_stream import open_tcp_stream as open_tcp_stream

from ._highlevel_open_tcp_listeners import open_tcp_listeners, serve_tcp
from ._highlevel_open_tcp_listeners import (
open_tcp_listeners as open_tcp_listeners,
serve_tcp as serve_tcp,
)

from ._highlevel_open_unix_stream import open_unix_socket
from ._highlevel_open_unix_stream import open_unix_socket as open_unix_socket

from ._highlevel_ssl_helpers import (
open_ssl_over_tcp_stream,
open_ssl_over_tcp_listeners,
serve_ssl_over_tcp,
open_ssl_over_tcp_stream as open_ssl_over_tcp_stream,
open_ssl_over_tcp_listeners as open_ssl_over_tcp_listeners,
serve_ssl_over_tcp as serve_ssl_over_tcp,
)

from ._core._multierror import MultiError as _MultiError
from ._core._multierror import NonBaseMultiError as _NonBaseMultiError

from ._deprecate import TrioDeprecationWarning
from ._deprecate import TrioDeprecationWarning as TrioDeprecationWarning

# Submodules imported by default
Comment thread
A5rocks marked this conversation as resolved.
from . import lowlevel
Expand All @@ -106,7 +123,7 @@
if False:
from . import testing

from . import _deprecate
from . import _deprecate as _deprecate

_deprecate.enable_attribute_deprecations(__name__)

Expand Down
159 changes: 159 additions & 0 deletions trio/_tests/check_type_completeness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
# this file is not run as part of the tests, instead it's run standalone from check.sh
import subprocess
import json
from pathlib import Path
import sys
import argparse

# the result file is not marked in MANIFEST.in so it's not included in the package
RESULT_FILE = Path(__file__).parent / "verify_types.json"
failed = False


# TODO: consider checking manually without `--ignoreexternal`, and/or
# removing it from the below call later on.
def run_pyright():
return subprocess.run(
["pyright", "--verifytypes=trio", "--outputjson", "--ignoreexternal"],
Comment thread
A5rocks marked this conversation as resolved.
capture_output=True,
)


def check_less_than(key, current_dict, last_dict, /, invert=False):
global failed
current = current_dict[key]
last = last_dict[key]
assert isinstance(current, (float, int))
assert isinstance(last, (float, int))
if current == last:
return
if (current > last) ^ invert:
failed = True
print("ERROR: ", end="")
if isinstance(current, float):
strcurrent = f"{current:.4}"
strlast = f"{last:.4}"
else:
strcurrent = str(current)
strlast = str(last)
print(
f"{key} has gone {'down' if current<last else 'up'} from {strlast} to {strcurrent}"
)


def check_zero(key, current_dict):
global failed
if current_dict[key] != 0:
failed = True
print(f"ERROR: {key} is {current_dict[key]}")


def main(args: argparse.Namespace) -> int:
print("*" * 20, "\nChecking type completeness hasn't gone down...")

res = run_pyright()
current_result = json.loads(res.stdout)
py_typed_file: Path | None = None

# check if py.typed file was missing
if (
current_result["generalDiagnostics"]
and current_result["generalDiagnostics"][0]["message"]
== "No py.typed file found"
):
print("creating py.typed")
py_typed_file = (
Path(current_result["typeCompleteness"]["packageRootDirectory"])
/ "py.typed"
)
py_typed_file.write_text("")

res = run_pyright()
current_result = json.loads(res.stdout)

if res.stderr:
print(res.stderr)

last_result = json.loads(RESULT_FILE.read_text())

for key in "errorCount", "warningCount", "informationCount":
check_zero(key, current_result["summary"])

for key, invert in (
("missingFunctionDocStringCount", False),
("missingClassDocStringCount", False),
("missingDefaultParamCount", False),
("completenessScore", True),
):
check_less_than(
key,
current_result["typeCompleteness"],
last_result["typeCompleteness"],
invert=invert,
)

for key, invert in (
("withUnknownType", False),
("withAmbiguousType", False),
("withKnownType", True),
):
check_less_than(
key,
current_result["typeCompleteness"]["exportedSymbolCounts"],
last_result["typeCompleteness"]["exportedSymbolCounts"],
invert=invert,
)

assert (
res.returncode != 0
), "Fully type complete! Delete this script and instead directly run `pyright --verifytypes=trio` (consider `--ignoreexternal`) in CI and checking exit code."

if args.overwrite_file:
print("Overwriting file")

# don't care about differences in time taken
del current_result["time"]
del current_result["summary"]["timeInSec"]

# don't fail on version diff so pyright updates can be automerged
del current_result["version"]

for key in (
# don't save path (because that varies between machines)
"moduleRootDirectory",
"packageRootDirectory",
"pyTypedPath",
):
del current_result["typeCompleteness"][key]

# prune the symbols to only be the name of the symbols with
# errors, instead of saving a huge file.
new_symbols = []
for symbol in current_result["typeCompleteness"]["symbols"]:
if symbol["diagnostics"]:
new_symbols.append(symbol["name"])
continue

current_result["typeCompleteness"]["symbols"] = new_symbols

with open(RESULT_FILE, "w") as file:
json.dump(current_result, file, sort_keys=True, indent=2)
# add newline at end of file so it's easier to manually modify
file.write("\n")

if py_typed_file is not None:
print("deleting py.typed")
py_typed_file.unlink()

print("*" * 20)

return int(failed)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool is a subclass of int so this isn't necessary. But maybe nice for clarity? Dunno.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, I guess it's my c++ programming leaking. But yeah I don't mind the int conversion



parser = argparse.ArgumentParser()
parser.add_argument("--overwrite-file", action="store_true", default=False)
args = parser.parse_args()

assert __name__ == "__main__", "This script should be run standalone"
sys.exit(main(args))
Loading