Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into freider/test-vanilla-…
Browse files Browse the repository at this point in the history
…event-loop-windows
  • Loading branch information
freider committed Nov 3, 2024
2 parents bd5abd3 + 8f8b6b8 commit b5c711f
Show file tree
Hide file tree
Showing 29 changed files with 150 additions and 135 deletions.
42 changes: 22 additions & 20 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,36 @@ on: push
jobs:
ruff:
name: Ruff
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.9"

- name: Install ruff
run: pip install ruff==0.2.0

python-version: 3.11
- name: Install dependencies
run: uv sync --only-group=lint
- name: Check lint with Ruff
run: ruff check .
run: uv run --only-group=lint ruff check .
- name: Check formatting with Ruff
run: uv run --only-group=lint ruff format --diff .

mypy:
name: MyPy
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.9"

- name: Install mypy
run: pip install mypy==1.8.0

python-version: 3.11
- name: Install dependencies
run: uv sync --only-group=lint
- name: Run
run: mypy synchronicity
run: uv run --only-group=lint mypy src/synchronicity
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
strategy:
fail-fast: false # run all variants across python versions/os to completion
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
os: ["ubuntu-latest"]
include:
- os: "macos-12" # x86-64
Expand All @@ -20,11 +20,13 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v3
- name: Install Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install . -r requirements.dev.txt
run: uv sync --group=dev
- name: Run tests with pytest
run: pytest -s
run: uv run --group=dev pytest -s
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ venv*
build
dist
*.iml
uv.lock
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ clean:
rm -rf build dist

build: clean
pip wheel -w dist --no-deps .
uv build

publish: build
twine upload dist/*
uv publish
41 changes: 34 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
[project]
name = "synchronicity"
version = "0.8.3"
version = "0.9.3"
description = "Export blocking and async library versions from a single async implementation"
readme = "README.md"
authors = [
{ name = "Modal Labs" }
]
requires-python = ">=3.8"
dependencies = [
"sigtools>=4.0.1",
"typing-extensions>=4.12.2",
]
classifiers = [
"Operating System :: OS Independent",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
]

dependencies = ["sigtools==4.0.1", "typing_extensions>=4.6"]
requires-python = ">=3.8"

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.setuptools.packages.find]
include = ["synchronicity*"]

[tool.ruff]
line-length = 120
Expand All @@ -30,3 +34,26 @@ select = ['E', 'F', 'W', 'I']
combine-as-imports = true
known-first-party = ["synchronicity"]
extra-standard-library = ["pytest"]

[tool.hatch.build.targets.sdist]
exclude = [
".*",
]

[dependency-groups]
dev = [
"pre-commit>=3.5.0",
{include-group = "lint"},
{include-group = "test"}
]
lint = [
"mypy-extensions>=1.0.0",
"mypy>=1.13.0",
"ruff>=0.7.0",
]
test = [
"console-ctrl>=0.1.0",
"gevent>=24.2.1; python_version < '3.13'",
"pytest>=8.3.3",
"pytest-asyncio>=0.24.0",
]
8 changes: 0 additions & 8 deletions requirements.dev.txt

This file was deleted.

3 changes: 3 additions & 0 deletions src/synchronicity/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .synchronizer import Synchronizer

__all__ = ["Synchronizer"]
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ def run(self, coro: typing.Awaitable[T]) -> T:
coro_task = asyncio.ensure_future(coro, loop=self._loop)

async def wrapper_coro():
# this wrapper is needed since run_coroutine_threadsafe *only* accepts coroutines
# this wrapper ensures that we won't reraise KeyboardInterrupt into
# the calling scope until all async finalizers in coro_task have
# finished executing. It even allows the coro to prevent cancellation
# and thereby ignoring the first keyboardinterrupt
return await coro_task

def _sigint_handler(signum, frame):
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,10 @@ def __get__(self, instance, owner=None):


class AsyncAndBlockingContextManager(typing_extensions.Protocol[CTX]):
def __enter__(self) -> CTX:
...
def __enter__(self) -> CTX: ...

async def __aenter__(self) -> CTX:
...
async def __aenter__(self) -> CTX: ...

def __exit__(self, typ, value, tb):
...
def __exit__(self, typ, value, tb): ...

async def __aexit__(self, typ, value, tb):
...
async def __aexit__(self, typ, value, tb): ...
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file added src/synchronicity/py.typed
Empty file.
File renamed without changes.
24 changes: 16 additions & 8 deletions synchronicity/type_stubs.py → src/synchronicity/type_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
from sigtools._signatures import EmptyAnnotation, UpgradedAnnotation, UpgradedParameter # type: ignore

import synchronicity
from synchronicity import Interface, combined_types, overload_tracking
from synchronicity import combined_types, overload_tracking
from synchronicity.annotations import evaluated_annotation
from synchronicity.interface import Interface
from synchronicity.synchronizer import (
SYNCHRONIZER_ATTR,
TARGET_INTERFACE_ATTR,
Expand Down Expand Up @@ -120,11 +121,11 @@ def _get_type_vars(typ, synchronizer, home_module):
ret = set()
if isinstance(typ, typing.TypeVar):
# check if it's translated (due to bounds= attributes etc.)
typ = synchronizer._translate_out(typ, Interface.BLOCKING)
typ = synchronizer._translate_out(typ)
ret.add(typ)
elif isinstance(typ, (typing_extensions.ParamSpecArgs, typing_extensions.ParamSpecKwargs)):
param_spec = origin
param_spec = synchronizer._translate_out(param_spec, Interface.BLOCKING)
param_spec = synchronizer._translate_out(param_spec)
ret.add(param_spec)
elif origin:
for arg in typing.get_args(typ):
Expand Down Expand Up @@ -577,16 +578,18 @@ def _translate_annotation_map_types(
if interface == Interface.BLOCKING:
# blocking interface special generic translations:
if origin == collections.abc.AsyncGenerator:
return typing.Generator[mapped_args + (None,)] # type: ignore
return typing.Generator[mapped_args + (None,)] # type: ignore[valid-type,misc]

if origin == contextlib.AbstractAsyncContextManager:
return combined_types.AsyncAndBlockingContextManager[mapped_args] # type: ignore
# TODO: in Python 3.13 mapped_args has a second argument for the exit type of the context
# manager, but we ignore that for now
return combined_types.AsyncAndBlockingContextManager[mapped_args[0]] # type: ignore[valid-type]

if origin == collections.abc.AsyncIterable:
return typing.Iterable[mapped_args] # type: ignore
return typing.Iterable[mapped_args] # type: ignore[valid-type]

if origin == collections.abc.AsyncIterator:
return typing.Iterator[mapped_args] # type: ignore
return typing.Iterator[mapped_args] # type: ignore[valid-type]

if origin == collections.abc.Awaitable:
return mapped_args[0]
Expand Down Expand Up @@ -710,7 +713,7 @@ def _formatannotation(self, annotation, base_module=None) -> str:
if annotation == Ellipsis:
return "..."
if isinstance(annotation, type) or isinstance(annotation, (TypeVar, typing_extensions.ParamSpec)):
if annotation == type(None): # check for "NoneType"
if annotation is type(None): # check for "NoneType"
return "None"
name = (
annotation.__qualname__ # type: ignore
Expand All @@ -730,10 +733,15 @@ def _formatannotation(self, annotation, base_module=None) -> str:
# e.g. first argument to typing.Callable
subargs = ",".join([self._formatannotation(arg) for arg in annotation])
return f"[{subargs}]"

return repr(annotation)

# generic:
origin_name = get_specific_generic_name(annotation)
if origin is contextlib.AbstractAsyncContextManager:
# python 3.13 adds a second optional exit arg, we only want to emit the first one
# to be backwards compatible
args = args[:1]

if (safe_get_module(annotation), origin_name) == ("typing", "Optional"):
# typing.Optional adds a None argument that we shouldn't include when formatting
Expand Down
2 changes: 0 additions & 2 deletions synchronicity/__init__.py

This file was deleted.

6 changes: 3 additions & 3 deletions test/async_wrap_test.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import inspect
import typing

from synchronicity import Interface, async_wrap
from synchronicity import async_wrap
from synchronicity.async_wrap import wraps_by_interface
from synchronicity.interface import Interface
from synchronicity.synchronizer import FunctionWithAio


Expand Down Expand Up @@ -30,8 +31,7 @@ def bar():

def test_wrap_asynccontextmanager_annotations():
@async_wrap.asynccontextmanager # this would not work with contextlib.asynccontextmanager
async def foo() -> typing.AsyncGenerator[int, None]:
...
async def foo() -> typing.AsyncGenerator[int, None]: ...

assert foo.__annotations__["return"] == typing.AsyncContextManager[int]

Expand Down
Loading

0 comments on commit b5c711f

Please sign in to comment.