Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make all features in Altair thread-safe #3589

Open
3 tasks
binste opened this issue Sep 12, 2024 · 1 comment
Open
3 tasks

Make all features in Altair thread-safe #3589

binste opened this issue Sep 12, 2024 · 1 comment

Comments

@binste
Copy link
Contributor

binste commented Sep 12, 2024

What is your suggestion?

Right now (version 5.4.0), Altair is mostly safe to use in a threading context. However, we could go through the whole library to make sure everything is covered and then advertise this. Features which might require adjustments/locks:

Have you considered any alternative solutions?

This is not urgent and we can leave it as-is. Users and developers of other libraries can work around these limitations with locks. But it would be easier if we do it in Altair itself.

@dangotbanned
Copy link
Member

dangotbanned commented Sep 12, 2024

If the stdlib considers this a thread safe counter, maybe that is an easy solution?

https://github.com/python/cpython/blob/6e06e01881dcffbeef5baac0c112ffb14cfa0b27/Lib/asyncio/tasks.py#L31-L34

# Helper to generate new task names
# This uses itertools.count() instead of a "+= 1" operation because the latter
# is not thread safe. See bpo-11866 for a longer explanation.
_task_name_counter = itertools.count(1).__next__

An example where I adapted this:

Code block
import itertools
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, ClassVar, Protocol, runtime_checkable

_counter_glob = itertools.count(1).__next__


@runtime_checkable
class _AsyncNamed(Protocol):
    # Mixin providing uniquely named instances.
    _counter_cls: ClassVar[Callable[[], int]]
    _name: str

    def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None:
        super().__init_subclass__(*args, **kwargs)
        cls._counter_cls = itertools.count(1).__next__

    def __init__(self) -> None:
        cls = type(self)
        self._name = f"{cls.__name__}-{cls._counter_cls()}-{_counter_glob()}"

    def _name_sub(self, obj: Any, /) -> str:
        other = obj.name if isinstance(obj, _AsyncNamed) else type(obj).__name__
        return f"{self.name} > {other}"

    @property
    def name(self) -> str:
        return self._name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants