diff --git a/tools/base/BUILD b/tools/base/BUILD index c7c1de9dcbe33..c1d243c119ed7 100644 --- a/tools/base/BUILD +++ b/tools/base/BUILD @@ -6,12 +6,10 @@ licenses(["notice"]) # Apache 2 envoy_package() -envoy_py_library("tools.base.abstract") - envoy_py_library( "tools.base.aio", deps = [ - ":functional", + requirement("aio.functional"), ], ) @@ -22,8 +20,6 @@ envoy_py_library( ], ) -envoy_py_library("tools.base.functional") - envoy_py_library( "tools.base.runner", deps = [ diff --git a/tools/base/abstract.py b/tools/base/abstract.py deleted file mode 100644 index 88d6f8ccb370a..0000000000000 --- a/tools/base/abstract.py +++ /dev/null @@ -1,165 +0,0 @@ -from abc import ABCMeta -from typing import Any, Dict, List, Tuple, Type, Union - - -class Implementer(type): - """Metaclass for implementers of an Abstract interface - - Any `Abstraction` classes that are listed in `__implements__` for the - class are added as bases (ie added to the class's inheritance). - - Any docs for methods are copied from the interface method to the - implementation if the implementation method has no docs of its own. - - For example: - - ``` - - from tools.base.abstract import Abstraction, Implementer - - - class AFoo(metaclass=Abstraction): - - @abstractmethod - def do_something(self): - \"""Do something\""" - raise NotImplementedError - - class Foo(metaclass=Implementer) - __implements__ = (AFoo, ) - - def do_something(self): - return "DONE" - ``` - Given the above, you should see that instantiating `Foo`: - - ``` - >>> isinstance(Foo(), AFoo) - True - >>> Foo().do_something.__doc__ - 'Do something' - ``` - """ - - @classmethod - def abstract_info(cls, abstract: "Abstraction") -> Tuple[str, Union[str, None], List[str]]: - """Information for a specific abstract implementation class - - For given abstract class, returns: - - - qualified class name - - class docstring - - abstract methods - """ - if not isinstance(abstract, Abstraction): - raise TypeError( - f"Implementers can only implement subclasses of `abstract.Abstraction`, unrecognized: '{abstract}'" - ) - methods: List[str] = [] - for method in getattr(abstract, "__abstractmethods__", []): - methods.append(method) - return f"{abstract.__module__}.{abstract.__name__}", abstract.__doc__, methods - - @classmethod - def add_docs(cls, clsdict: Dict, klass: "Implementer") -> None: - """Add docs to the implementation class - - If the implementation class has no docstring, then a docstring is generated with - the format: - - ``` - Implements: foo.bar.ABaz - An implementer of the ABaz protocol... - - Implements: foo.bar.AOtherBaz - An implementer of the AOtherBaz protocol... - ``` - - For each of the methods that are marked abstract in any of the abstract - classes, if the method in the implementation class has no docstring the docstring - is resolved from the abstract methods, using standard mro. - """ - abstract_docs, abstract_methods = cls.implementation_info(clsdict) - if not klass.__doc__: - klass.__doc__ = "\n".join(f"Implements: {k}\n{v}\n" for k, v in abstract_docs.items()) - for abstract_method, abstract_klass in abstract_methods.items(): - method = getattr(klass, abstract_method, None) - if not method: - # this will not instantiate, so bail now - return - # Only set the doc for the method if its not already set. - # `@classmethod` `__doc__`s are immutable, so skip them. - if not method.__doc__ and not hasattr(method, "__self__"): - method.__doc__ = getattr(abstract_klass, abstract_method).__doc__ - - @classmethod - def get_bases(cls, bases: Tuple[Type, ...], clsdict: Dict) -> Tuple[Type, ...]: - """Returns a tuple of base classes, with `__implements__` classes included""" - return bases + tuple(x for x in clsdict["__implements__"] if x not in bases) - - @classmethod - def implementation_info(cls, clsdict: Dict) -> Tuple[Dict[str, str], Dict[str, Type]]: - """Returns 2 dictionaries - - - abstract_docs: abstract docs for all abstract classes - - abstract_methods: resolved abstract methods -> abstract class - """ - abstract_docs: Dict[str, str] = {} - abstract_methods: Dict[str, Type] = {} - for abstract in reversed(clsdict["__implements__"]): - name, docs, methods = cls.abstract_info(abstract) - for method in methods: - abstract_methods[method] = abstract - if docs: - abstract_docs[name] = docs - return abstract_docs, abstract_methods - - def __new__(cls, clsname: str, bases: Tuple[Type, ...], clsdict: Dict) -> "Implementer": - """Create a new Implementer class""" - if "__implements__" not in clsdict: - return super().__new__(cls, clsname, bases, clsdict) - klass = super().__new__(cls, clsname, cls.get_bases(bases, clsdict), clsdict) - cls.add_docs(clsdict, klass) - return klass - - -class Abstraction(Implementer, ABCMeta): - pass - - -def implementer(implements): - """Decorator for implementers - - Assuming you have an abstract class `AFoo` which has a `metaclass` of - type `Abstraction`, it can be used as follows: - - ``` - from tools.base import abstract - - @abstract.implementer(AFoo) - class Foo: - - def bar(self): - return "BAZ" - - ``` - - """ - - if not isinstance(implements, (tuple, list, set)): - implements = (implements,) - - def wrapper(klass: Any) -> Implementer: - # This is a v annoying workaround for mypy, see: - # https://github.com/python/mypy/issues/9183 - _klass: Any = klass - - class Implementation(_klass, metaclass=Implementer): - __implements__ = implements - __doc__ = _klass.__doc__ - - Implementation.__qualname__ = klass.__name__ - Implementation.__name__ = klass.__name__ - return Implementation - - return wrapper diff --git a/tools/base/aio.py b/tools/base/aio.py index 5ef5ea2a04f08..a07787c31133f 100644 --- a/tools/base/aio.py +++ b/tools/base/aio.py @@ -9,7 +9,7 @@ Any, AsyncGenerator, AsyncIterable, AsyncIterator, Awaitable, Iterable, Iterator, List, Optional, Union) -from tools.base.functional import async_property +from aio.functional import async_property class ConcurrentError(Exception): diff --git a/tools/base/functional.py b/tools/base/functional.py deleted file mode 100644 index 4024bfcca9244..0000000000000 --- a/tools/base/functional.py +++ /dev/null @@ -1,87 +0,0 @@ -# -# Functional utilities -# - -import inspect -from typing import Any, Callable, Optional - - -class NoCache(Exception): - pass - - -class async_property: # noqa: N801 - name = None - cache_name = "__async_prop_cache__" - _instance = None - - # If the decorator is called with `kwargs` then `fun` is `None` - # and instead `__call__` is triggered with `fun` - def __init__(self, fun: Optional[Callable] = None, cache: bool = False): - self.cache = cache - self._fun = fun - self.name = getattr(fun, "__name__", None) - self.__doc__ = getattr(fun, '__doc__') - - def __call__(self, fun: Callable) -> 'async_property': - self._fun = fun - self.name = self.name or fun.__name__ - self.__doc__ = getattr(fun, '__doc__') - return self - - def __get__(self, instance: Any, cls=None) -> Any: - if instance is None: - return self - self._instance = instance - if inspect.isasyncgenfunction(self._fun): - return self.async_iter_result() - return self.async_result() - - def fun(self, *args, **kwargs): - if self._fun: - return self._fun(*args, **kwargs) - - @property - def prop_cache(self) -> dict: - return getattr(self._instance, self.cache_name, {}) - - # An async wrapper function to return the result - # This is returned when the prop is called if the wrapped - # method is an async generator - async def async_iter_result(self): - # retrieve the value from cache if available - try: - result = self.get_cached_prop() - except (NoCache, KeyError): - result = None - - if result is None: - result = self.set_prop_cache(self.fun(self._instance)) - - async for item in result: - yield item - - # An async wrapper function to return the result - # This is returned when the prop is called - async def async_result(self) -> Any: - # retrieve the value from cache if available - try: - return self.get_cached_prop() - except (NoCache, KeyError): - pass - - # derive the result, set the cache if required, and return the result - return self.set_prop_cache(await self.fun(self._instance)) - - def get_cached_prop(self) -> Any: - if not self.cache: - raise NoCache - return self.prop_cache[self.name] - - def set_prop_cache(self, result: Any) -> Any: - if not self.cache: - return result - cache = self.prop_cache - cache[self.name] = result - setattr(self._instance, self.cache_name, cache) - return result diff --git a/tools/base/requirements.txt b/tools/base/requirements.txt index 20ae6d1dc142d..cc77ac7ae53f8 100644 --- a/tools/base/requirements.txt +++ b/tools/base/requirements.txt @@ -4,6 +4,9 @@ # # pip-compile --allow-unsafe --generate-hashes tools/base/requirements.txt # +aio.functional==0.0.4 \ + --hash=sha256:e4293287491c4f592bcaad69eb2d75ebe988210314cc3dce5d5c61e8992471e0 + # via -r tools/base/requirements.txt colorama==0.4.4 \ --hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b \ --hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 diff --git a/tools/base/tests/test_abstract.py b/tools/base/tests/test_abstract.py deleted file mode 100644 index c996233606a04..0000000000000 --- a/tools/base/tests/test_abstract.py +++ /dev/null @@ -1,253 +0,0 @@ -from abc import abstractmethod -from random import random -from unittest.mock import MagicMock - -import pytest - -from tools.base import abstract - - -def test_implementer_constructor(): - with pytest.raises(TypeError): - abstract.Implementer() - - assert issubclass(abstract.Implementer, type) - - -@pytest.mark.parametrize("isinst", [True, False]) -def test_implementer_abstract_info(patches, isinst): - abstract_methods = [f"METHOD{i}" for i in range(0, 5)] - abstract_class = MagicMock() - abstract_class.__abstractmethods__ = abstract_methods - abstract_class.__name__ = MagicMock() - - patched = patches( - "isinstance", - prefix="tools.base.abstract") - - with patched as (m_inst, ): - m_inst.return_value = isinst - if not isinst: - with pytest.raises(TypeError) as e: - abstract.Implementer.abstract_info(abstract_class) - assert ( - e.value.args[0] - == f"Implementers can only implement subclasses of `abstract.Abstraction`, unrecognized: '{abstract_class}'") - else: - assert ( - abstract.Implementer.abstract_info(abstract_class) - == (f"{abstract_class.__module__}.{abstract_class.__name__}", - abstract_class.__doc__, - abstract_methods)) - - -@pytest.mark.parametrize("methods", [[], [f"METHOD{i}" for i in range(0, 2)], [f"METHOD{i}" for i in range(0, 5)]]) -@pytest.mark.parametrize("has_docs", [[], [f"METHOD{i}" for i in range(0, 2)], [f"METHOD{i}" for i in range(0, 5)]]) -@pytest.mark.parametrize("is_classmethod", [[], [f"METHOD{i}" for i in range(0, 2)], [f"METHOD{i}" for i in range(0, 5)]]) -@pytest.mark.parametrize("doc", [True, False]) -def test_implementer_add_docs(patches, methods, has_docs, doc, is_classmethod): - abstract_docs = {f"DOC{i}": int(random() * 10) for i in range(0, 5)} - abstract_methods = {f"METHOD{i}": MagicMock() for i in range(0, 5)} - - clsdict = MagicMock() - klass = MagicMock() - - if not doc: - klass.__doc__ = "" - - for method, abstract_klass in abstract_methods.items(): - getattr(abstract_klass, method).__doc__ = "KLASS DOCS" - if method not in methods: - delattr(klass, method) - continue - if method not in has_docs: - getattr(klass, method).__doc__ = "" - if method in is_classmethod: - getattr(klass, method).__self__ = "CLASSMETHOD_CLASS" - - patched = patches( - "Implementer.implementation_info", - prefix="tools.base.abstract") - - with patched as (m_info, ): - m_info.return_value = abstract_docs, abstract_methods - assert not abstract.Implementer.add_docs(clsdict, klass) - - assert ( - klass.__doc__ - == (MagicMock.__doc__ - if doc - else "\n".join(f"Implements: {k}\n{v}\n" for k, v in abstract_docs.items()))) - - failed = False - for abstract_method, abstract_klass in abstract_methods.items(): - if not abstract_method in methods: - continue - expected = MagicMock.__doc__ - if abstract_method in is_classmethod and abstract_method not in has_docs: - expected = "" - elif abstract_method not in has_docs: - expected = "KLASS DOCS" - assert ( - getattr(klass, abstract_method).__doc__ - == expected) - - -@pytest.mark.parametrize("bases", [(), ("A", "B", "C"), ("A", "C")]) -@pytest.mark.parametrize("implements", [(), ("A", "B", "C"), ("A", "C")]) -def test_implementer_get_bases(bases, implements): - clsdict = dict(__implements__=implements) - assert ( - abstract.Implementer.get_bases(bases, clsdict) - == bases + tuple(x for x in clsdict["__implements__"] if x not in bases)) - - -@pytest.mark.parametrize("has_docs", ["odd", "even"]) -def test_implementer_implementation_info(patches, has_docs): - patched = patches( - "Implementer.abstract_info", - prefix="tools.base.abstract") - - def iter_implements(): - for x in range(0, 5): - yield f"ABSTRACT{x}" - - implements = list(iter_implements()) - clsdict = dict(__implements__=implements) - - def abstract_info(abstract): - x = int(abstract[-1]) - methods = [f"METHOD{i}" for i in range(0, x + 1)] - docs = ( - x % 2 - if has_docs == "even" - else not x % 2) - return abstract, docs, methods - - with patched as (m_info, ): - m_info.side_effect = abstract_info - docs, methods = abstract.Implementer.implementation_info(clsdict) - - def oddeven(i): - return ( - i % 2 - if has_docs == "even" - else not i % 2) - - assert docs == {f"ABSTRACT{i}": 1 for i in range(0, 5) if oddeven(i)} - assert ( - methods - == {f'METHOD{i}': f'ABSTRACT{i}' for i in range(0, 5)}) - - -@pytest.mark.parametrize("has_implements", [True, False]) -def test_implementer_dunder_new(patches, has_implements): - patched = patches( - "super", - "Implementer.add_docs", - prefix="tools.base.abstract") - bases = MagicMock() - clsdict = dict(FOO="BAR") - - if has_implements: - clsdict["__implements__"] = "IMPLEMENTS" - - class SubImplementer(abstract.Implementer): - pass - - class Super: - - def __new__(cls, *args): - if not args: - return cls - return "NEW" - - with patched as (m_super, m_docs): - m_super.side_effect = Super - assert ( - abstract.Implementer.__new__(abstract.Implementer, "NAME", bases, clsdict) - == "NEW") - - if has_implements: - assert ( - list(m_docs.call_args) - == [(clsdict, "NEW"), {}]) - else: - assert not m_docs.called - - -class AFoo(metaclass=abstract.Abstraction): - - @classmethod - @abstractmethod - def do_something_classy(cls): - pass - - @abstractmethod - def do_something(self): - """Do something""" - raise NotImplementedError - - -class ABar(metaclass=abstract.Abstraction): - - @abstractmethod - def do_something_else(self): - """Do something else""" - raise NotImplementedError - - -class Baz: - - def do_nothing(self): - pass - - -@pytest.mark.parametrize("implements", [(), AFoo, (AFoo, ), (AFoo, ABar), (AFoo, ABar, Baz), Baz]) -def test_implementer_decorator(patches, implements): - iterable_implements = ( - implements - if isinstance(implements, tuple) - else (implements, )) - should_fail = any( - not isinstance(impl, abstract.Abstraction) - for impl - in iterable_implements) - - if should_fail: - with pytest.raises(TypeError): - _implementer(implements) - return - - implementer = _implementer(implements) - for impl in iterable_implements: - assert issubclass(implementer, impl) - assert ( - implementer.__name__ - == implementer.__qualname__ - == "ImplementationOfAnImplementer") - assert ( - implementer.__doc__ - == 'A test implementation of an implementer') - - -def _implementer(implements): - - @abstract.implementer(implements) - class ImplementationOfAnImplementer: - """A test implementation of an implementer""" - - @classmethod - def do_something_classy(cls): - pass - - def do_something(self): - pass - - def do_something_else(self): - pass - - def do_nothing(self): - pass - - return ImplementationOfAnImplementer diff --git a/tools/base/tests/test_functional.py b/tools/base/tests/test_functional.py deleted file mode 100644 index c8631714897b1..0000000000000 --- a/tools/base/tests/test_functional.py +++ /dev/null @@ -1,119 +0,0 @@ -import types -from unittest.mock import AsyncMock - -import pytest - -from tools.base import functional - - -@pytest.mark.asyncio -@pytest.mark.parametrize("cache", [None, True, False]) -@pytest.mark.parametrize("raises", [True, False]) -@pytest.mark.parametrize("result", [None, False, "X", 23]) -async def test_functional_async_property(cache, raises, result): - m_async = AsyncMock(return_value=result) - - class SomeError(Exception): - pass - - if cache is None: - decorator = functional.async_property - iter_decorator = functional.async_property - else: - decorator = functional.async_property(cache=cache) - iter_decorator = functional.async_property(cache=cache) - - items = [f"ITEM{i}" for i in range(0, 5)] - - class Klass: - - @decorator - async def prop(self): - """This prop deserves some docs""" - if raises: - await m_async() - raise SomeError("AN ERROR OCCURRED") - else: - return await m_async() - - @iter_decorator - async def iter_prop(self): - """This prop also deserves some docs""" - if raises: - await m_async() - raise SomeError("AN ITERATING ERROR OCCURRED") - result = await m_async() - for item in items: - yield item, result - - klass = Klass() - - # The class.prop should be an instance of async_prop - # and should have the name and docs of the wrapped method. - assert isinstance( - type(klass).prop, - functional.async_property) - assert ( - type(klass).prop.__doc__ - == "This prop deserves some docs") - assert ( - type(klass).prop.name - == "prop") - - if raises: - with pytest.raises(SomeError) as e: - await klass.prop - - with pytest.raises(SomeError) as e2: - async for result in klass.iter_prop: - pass - - assert ( - e.value.args[0] - == 'AN ERROR OCCURRED') - assert ( - e2.value.args[0] - == 'AN ITERATING ERROR OCCURRED') - assert ( - list(m_async.call_args_list) - == [[(), {}]] * 2) - return - - # results can be repeatedly awaited - assert await klass.prop == result - assert await klass.prop == result - - # results can also be repeatedly iterated - results1 = [] - async for returned_result in klass.iter_prop: - results1.append(returned_result) - assert results1 == [(item, result) for item in items] - - results2 = [] - async for returned_result in klass.iter_prop: - results2.append(returned_result) - - if not cache: - assert results2 == results1 - assert ( - list(list(c) for c in m_async.call_args_list) - == [[(), {}]] * 4) - assert not hasattr(klass, functional.async_property.cache_name) - return - - # with cache we can keep awaiting the result but the fun - # is still only called once - assert await klass.prop == result - assert await klass.prop == result - assert ( - list(list(c) for c in m_async.call_args_list) - == [[(), {}]] * 2) - - iter_prop = getattr(klass, functional.async_property.cache_name)["iter_prop"] - assert isinstance(iter_prop, types.AsyncGeneratorType) - assert ( - getattr(klass, functional.async_property.cache_name) - == dict(prop=m_async.return_value, iter_prop=iter_prop)) - - # cached iterators dont give any more results once they are done - assert results2 == [] diff --git a/tools/github/release/BUILD b/tools/github/release/BUILD index 2f68200ffc6b2..d92b9c39b92b4 100644 --- a/tools/github/release/BUILD +++ b/tools/github/release/BUILD @@ -2,6 +2,7 @@ load("@github_pip3//:requirements.bzl", "requirement") load("@rules_python//python:defs.bzl", "py_library") load("//bazel:envoy_build_system.bzl", "envoy_package") load("//tools/base:envoy_python.bzl", "envoy_py_library") +load("@base_pip3//:requirements.bzl", base_requirement = "requirement") licenses(["notice"]) # Apache 2 @@ -11,9 +12,9 @@ py_library( name = "abstract", srcs = ["abstract.py"], deps = [ - "//tools/base:abstract", - "//tools/base:functional", + "//tools/base:aio", "//tools/base:utils", + requirement("abstracts"), requirement("aiohttp"), requirement("gidgethub"), requirement("packaging"), @@ -30,9 +31,9 @@ envoy_py_library( deps = [ ":abstract", ":exceptions", - "//tools/base:abstract", - "//tools/base:functional", "//tools/base:utils", + base_requirement("aio.functional"), + requirement("abstracts"), requirement("aiohttp"), requirement("gidgethub"), requirement("packaging"), @@ -45,8 +46,8 @@ envoy_py_library( ":abstract", ":exceptions", "//tools/base:aio", - "//tools/base:functional", "//tools/base:utils", + base_requirement("aio.functional"), requirement("gidgethub"), ], ) diff --git a/tools/github/release/abstract.py b/tools/github/release/abstract.py index 1542e0a7db149..b65f5fd4b02dd 100644 --- a/tools/github/release/abstract.py +++ b/tools/github/release/abstract.py @@ -13,11 +13,12 @@ import gidgethub.abc -from tools.base import abstract -from tools.base.functional import async_property +import abstracts +from aio.functional import async_property -class AGithubReleaseAssets(metaclass=abstract.Abstraction): + +class AGithubReleaseAssets(metaclass=abstracts.Abstraction): """Base class for Github release assets pusher/fetcher""" @abstractmethod @@ -47,7 +48,7 @@ async def awaitables( raise NotImplementedError -class AGithubReleaseAssetsFetcher(AGithubReleaseAssets, metaclass=abstract.Abstraction): +class AGithubReleaseAssetsFetcher(AGithubReleaseAssets, metaclass=abstracts.Abstraction): """Fetcher of Github release assets""" @abstractmethod @@ -93,7 +94,7 @@ async def save(self, asset_type: str, name: str, raise NotImplementedError -class AGithubReleaseAssetsPusher(AGithubReleaseAssets, metaclass=abstract.Abstraction): +class AGithubReleaseAssetsPusher(AGithubReleaseAssets, metaclass=abstracts.Abstraction): """Pusher of Github release assets""" @abstractmethod @@ -107,7 +108,7 @@ async def upload(self, artefact: pathlib.Path, url: str) -> Dict[str, Union[str, raise NotImplementedError -class AGithubRelease(metaclass=abstract.Abstraction): +class AGithubRelease(metaclass=abstracts.Abstraction): """A Github tagged release version Provides CRUD operations for a release and its assets, and therefore @@ -207,7 +208,7 @@ async def upload_url(self) -> str: raise NotImplementedError -class AGithubReleaseManager(metaclass=abstract.Abstraction): +class AGithubReleaseManager(metaclass=abstracts.Abstraction): """This utility wraps the github API to provide the ability to create and manage releases and release assets. diff --git a/tools/github/release/manager.py b/tools/github/release/manager.py index 7c465e421d5cf..753644ce4e9c7 100644 --- a/tools/github/release/manager.py +++ b/tools/github/release/manager.py @@ -12,8 +12,9 @@ import gidgethub.abc import gidgethub.aiohttp -from tools.base import abstract -from tools.base.functional import async_property +import abstracts + +from aio.functional import async_property from tools.github.release.abstract import AGithubRelease, AGithubReleaseManager from tools.github.release.exceptions import GithubReleaseError @@ -22,7 +23,7 @@ VERSION_MIN = packaging.version.Version("0") -@abstract.implementer(AGithubReleaseManager) +@abstracts.implementer(AGithubReleaseManager) class GithubReleaseManager: _version_re = r"v(\w+)" diff --git a/tools/github/release/release.py b/tools/github/release/release.py index 8963af080f316..76c88a8f4e36f 100644 --- a/tools/github/release/release.py +++ b/tools/github/release/release.py @@ -9,8 +9,11 @@ import gidgethub.abc import gidgethub.aiohttp -from tools.base import abstract, aio -from tools.base.functional import async_property +import abstracts + +from aio.functional import async_property + +from tools.base import aio from tools.github.release.abstract import ( AGithubRelease, AGithubReleaseAssetsFetcher, AGithubReleaseAssetsPusher, AGithubReleaseManager) @@ -18,7 +21,7 @@ from tools.github.release.exceptions import GithubReleaseError -@abstract.implementer(AGithubRelease) +@abstracts.implementer(AGithubRelease) class GithubRelease: file_exts = {"deb", "changes", "rpm"} diff --git a/tools/github/release/tests/test_manager.py b/tools/github/release/tests/test_manager.py index d0ae588529284..c7f4fa6629f8d 100644 --- a/tools/github/release/tests/test_manager.py +++ b/tools/github/release/tests/test_manager.py @@ -5,7 +5,8 @@ import packaging.version -from tools.base.functional import async_property +from aio.functional import async_property + from tools.github.release import manager, exceptions as github_errors diff --git a/tools/github/release/tests/test_release.py b/tools/github/release/tests/test_release.py index 8edfee419039e..96308443ff816 100644 --- a/tools/github/release/tests/test_release.py +++ b/tools/github/release/tests/test_release.py @@ -5,8 +5,9 @@ import gidgethub +from aio.functional import async_property + from tools.base import aio -from tools.base.functional import async_property from tools.github.release import exceptions as github_errors, release as github_release diff --git a/tools/github/requirements.txt b/tools/github/requirements.txt index f949022666fdf..5cc6ffcb87a5c 100644 --- a/tools/github/requirements.txt +++ b/tools/github/requirements.txt @@ -4,6 +4,9 @@ # # pip-compile --generate-hashes tools/github/requirements.txt # +abstracts==0.0.8 \ + --hash=sha256:a0aedf05742059764f7f7975a0a0fb2b57598a3175a635bbefc7343533fd8bce + # via -r tools/github/requirements.txt aiohttp==3.7.4.post0 \ --hash=sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe \ --hash=sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe \ @@ -46,11 +49,15 @@ aiohttp==3.7.4.post0 \ async-timeout==3.0.1 \ --hash=sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f \ --hash=sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3 - # via aiohttp + # via + # -r tools/github/requirements.txt + # aiohttp attrs==21.2.0 \ --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \ --hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb - # via aiohttp + # via + # -r tools/github/requirements.txt + # aiohttp cffi==1.14.6 \ --hash=sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d \ --hash=sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771 \ @@ -97,11 +104,15 @@ cffi==1.14.6 \ --hash=sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c \ --hash=sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5 \ --hash=sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69 - # via cryptography + # via + # -r tools/github/requirements.txt + # cryptography chardet==4.0.0 \ --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 - # via aiohttp + # via + # -r tools/github/requirements.txt + # aiohttp cryptography==3.4.7 \ --hash=sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d \ --hash=sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959 \ @@ -117,7 +128,9 @@ cryptography==3.4.7 \ --hash=sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca \ --hash=sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d \ --hash=sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9 - # via pyjwt + # via + # -r tools/github/requirements.txt + # pyjwt gidgethub==5.0.1 \ --hash=sha256:3efbd6998600254ec7a2869318bd3ffde38edc3a0d37be0c14bc46b45947b682 \ --hash=sha256:67245e93eb0918b37df038148af675df43b62e832c529d7f859f6b90d9f3e70d @@ -125,7 +138,9 @@ gidgethub==5.0.1 \ idna==3.2 \ --hash=sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a \ --hash=sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3 - # via yarl + # via + # -r tools/github/requirements.txt + # yarl multidict==5.1.0 \ --hash=sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a \ --hash=sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93 \ @@ -165,6 +180,7 @@ multidict==5.1.0 \ --hash=sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281 \ --hash=sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80 # via + # -r tools/github/requirements.txt # aiohttp # yarl packaging==21.0 \ @@ -174,24 +190,34 @@ packaging==21.0 \ pycparser==2.20 \ --hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \ --hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705 - # via cffi + # via + # -r tools/github/requirements.txt + # cffi pyjwt[crypto]==2.1.0 \ --hash=sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1 \ --hash=sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130 - # via gidgethub + # via + # -r tools/github/requirements.txt + # gidgethub pyparsing==2.4.7 \ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b - # via packaging + # via + # -r tools/github/requirements.txt + # packaging typing-extensions==3.10.0.0 \ --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 - # via aiohttp + # via + # -r tools/github/requirements.txt + # aiohttp uritemplate==3.0.1 \ --hash=sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f \ --hash=sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae - # via gidgethub + # via + # -r tools/github/requirements.txt + # gidgethub yarl==1.6.3 \ --hash=sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e \ --hash=sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434 \ @@ -230,4 +256,6 @@ yarl==1.6.3 \ --hash=sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c \ --hash=sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a \ --hash=sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71 - # via aiohttp + # via + # -r tools/github/requirements.txt + # aiohttp