Skip to content

Commit

Permalink
Add py.typed marker.
Browse files Browse the repository at this point in the history
This required moving away from using a single module, a known limitation
being currently visited in python/typing#1333.
  • Loading branch information
aebrahim committed Sep 6, 2023
1 parent aeee693 commit 2a9a327
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 11 deletions.
26 changes: 15 additions & 11 deletions once.py → once/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
"""Utility for initialization ensuring functions are called only once."""
import abc
import inspect
import collections.abc
import functools
import inspect
import threading
import typing
import weakref


def _new_lock() -> threading.Lock:
return threading.Lock()


def _is_method(func):
def _is_method(func: collections.abc.Callable):
"""Determine if a function is a method on a class."""
if isinstance(func, (classmethod, staticmethod)):
return True
Expand All @@ -21,22 +23,22 @@ def _is_method(func):
class _OnceBase(abc.ABC):
"""Abstract Base Class for once function decorators."""

def __init__(self, func):
def __init__(self, func: collections.abc.Callable):
self._inspect_function(func)
functools.update_wrapper(self, func)
self.lock = _new_lock()
self.called = False
self.return_value = None
self.return_value: typing.Any = None
self.func = func

@abc.abstractmethod
def _inspect_function(self, func):
def _inspect_function(self, func: collections.abc.Callable):
"""Inspect the passed-in function to ensure it can be wrapped.
This function should raise a SyntaxError if the passed-in function is
not suitable."""

def _execute_call_once(self, func, *args, **kwargs):
def _execute_call_once(self, func: collections.abc.Callable, *args, **kwargs):
if self.called:
return self.return_value
with self.lock:
Expand Down Expand Up @@ -66,7 +68,7 @@ class once(_OnceBase): # pylint: disable=invalid-name
value will never be deleted.
"""

def _inspect_function(self, func):
def _inspect_function(self, func: collections.abc.Callable):
if _is_method(func):
raise SyntaxError(
"Attempting to use @once.once decorator on method "
Expand Down Expand Up @@ -102,12 +104,14 @@ def __get__(self, obj, cls):
class once_per_instance(_OnceBase): # pylint: disable=invalid-name
"""A version of once for class methods which runs once per instance."""

def __init__(self, func):
def __init__(self, func: collections.abc.Callable):
super().__init__(func)
self.return_value = weakref.WeakKeyDictionary()
self.inflight_lock = {}
self.return_value: weakref.WeakKeyDictionary[
typing.Any, typing.Any
] = weakref.WeakKeyDictionary()
self.inflight_lock: typing.Dict[typing.Any, threading.Lock] = {}

def _inspect_function(self, func):
def _inspect_function(self, func: collections.abc.Callable):
if isinstance(func, (classmethod, staticmethod)):
raise SyntaxError("Must use @once.once_per_class on classmethod and staticmethod")
if not _is_method(func):
Expand Down
Empty file added once/py.typed
Empty file.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[tool.hatch.build]
include = ["once/*"]

[tool.hatch.version]
source = "vcs"

Expand Down

0 comments on commit 2a9a327

Please sign in to comment.