Add stubs for fanstatic (#9931)

This commit is contained in:
David Salvisberg
2023-11-22 00:13:45 +01:00
committed by GitHub
parent a9fc14a811
commit e6c3219586
12 changed files with 700 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# Error: is not present in stub
# =============================
# These are methods and attributes that really should have been
# prefixed with a `_`, since they should only really be used
# internally
fanstatic.Library.init_library_nr
fanstatic.core.Asset.init_dependency_nr
fanstatic.core.Library.init_library_nr
# In order to catch errors where DummyNeededResources would be called
# with clear/library_url/resources these methods were dropped from the
# stub. We would prefer to use @type_error() once that is an option
fanstatic.core.DummyNeededResources.clear
fanstatic.core.DummyNeededResources.library_url
fanstatic.core.DummyNeededResources.resources
# Error: is inconsistent
# ======================
# The core API for Dependable is a bit annoying, since the base class
# should really be abstract and instead defines some attributes as
# None, even though all subclasses populate them, so these have been
# made abstract to make defining correct subclasses more easy
fanstatic.Group.depends
fanstatic.Group.resources
fanstatic.Group.supports
fanstatic.core.Asset.depends
fanstatic.core.Asset.resources
fanstatic.core.Asset.supports
fanstatic.core.Dependable.depends
fanstatic.core.Dependable.resources
fanstatic.core.Dependable.supports
fanstatic.core.Group.depends
fanstatic.core.Group.resources
fanstatic.core.Group.supports
# The API for Compiler has very much the same problem, so these are
# some more attributes/methods that have been made abstract for the
# purposes of type checking
fanstatic.Compiler.name
fanstatic.Compiler.source_extension
fanstatic.Minifier.name
fanstatic.Minifier.source_extension
fanstatic.Minifier.target_extension
fanstatic.compiler.CommandlineBase.command
fanstatic.compiler.Compiler.name
fanstatic.compiler.Compiler.source_extension
fanstatic.compiler.Minifier.name
fanstatic.compiler.Minifier.source_extension
fanstatic.compiler.Minifier.target_extension
fanstatic.compiler.NullCompiler.name
fanstatic.registry.Registry.ENTRY_POINT
# This is only inconsistent because the library authors set this
# attribute to `None` on the class, so they could assign a docstring
# to it. `__init__` will always populate this attribute with a `str`
fanstatic.Library.path
fanstatic.core.Library.path
# Error: variable differs from runtime type
# ======================
# These are some sentinel objects which use the NewType pattern to create a
# distinct type
fanstatic.compiler.SOURCE
fanstatic.compiler.TARGET
fanstatic.core.NOTHING
fanstatic.core.REQUIRED_DEFAULT_MARKER
# Error: is not present at runtime
# ================================
# See above, defining correct subclasses is more easy with abstract
# properties on the superclass
fanstatic.injector.InjectorPlugin.name
# Error: failed to find stubs
# ===========================
# Tests should not be part of the stubs
fanstatic.tests.*

View File

@@ -0,0 +1,3 @@
version = "1.4.*"
upstream_repository = "https://github.com/zopefoundation/fanstatic"
requires = ["types-setuptools", "types-WebOb"]

View File

@@ -0,0 +1,43 @@
from fanstatic.compiler import Compiler as Compiler, Minifier as Minifier, sdist_compile as sdist_compile
from fanstatic.core import (
BUNDLE_PREFIX as BUNDLE_PREFIX,
DEBUG as DEBUG,
DEFAULT_SIGNATURE as DEFAULT_SIGNATURE,
MINIFIED as MINIFIED,
NEEDED as NEEDED,
VERSION_PREFIX as VERSION_PREFIX,
ConfigurationError as ConfigurationError,
Group as Group,
GroupResource as GroupResource,
Library as Library,
LibraryDependencyCycleError as LibraryDependencyCycleError,
NeededResources as NeededResources,
Resource as Resource,
Slot as Slot,
SlotError as SlotError,
UnknownResourceError as UnknownResourceError,
UnknownResourceExtension as UnknownResourceExtension,
UnknownResourceExtensionError as UnknownResourceExtensionError,
clear_needed as clear_needed,
del_needed as del_needed,
get_needed as get_needed,
init_needed as init_needed,
register_inclusion_renderer as register_inclusion_renderer,
set_auto_register_library as set_auto_register_library,
set_resource_file_existence_checking as set_resource_file_existence_checking,
)
from fanstatic.inclusion import Inclusion as Inclusion, bundle_resources as bundle_resources, sort_resources as sort_resources
from fanstatic.injector import Injector as Injector, make_injector as make_injector
from fanstatic.publisher import (
Delegator as Delegator,
LibraryPublisher as LibraryPublisher,
Publisher as Publisher,
make_publisher as make_publisher,
)
from fanstatic.registry import (
CompilerRegistry as CompilerRegistry,
LibraryRegistry as LibraryRegistry,
MinifierRegistry as MinifierRegistry,
get_library_registry as get_library_registry,
)
from fanstatic.wsgi import Fanstatic as Fanstatic, Serf as Serf, make_fanstatic as make_fanstatic, make_serf as make_serf

View File

@@ -0,0 +1,10 @@
from _typeshed import GenericPath, StrOrBytesPath, StrPath
from collections.abc import Iterator
from typing import AnyStr
VCS_NAMES: list[str]
IGNORED_EXTENSIONS: list[str]
def list_directory(path: GenericPath[AnyStr], include_directories: bool = True) -> Iterator[AnyStr]: ...
def mtime(path: StrOrBytesPath) -> str: ...
def md5(path: StrPath) -> str: ...

View File

@@ -0,0 +1,125 @@
from _typeshed import StrOrBytesPath
from abc import abstractmethod
from logging import Logger
from subprocess import Popen
from typing import Any, ClassVar, NewType
from typing_extensions import Literal
import setuptools.command.sdist
from fanstatic.core import Resource
logger: Logger
class CompilerError(Exception): ...
class Compiler:
@property
@abstractmethod
def name(self) -> str: ...
@property
@abstractmethod
def source_extension(self) -> str: ...
def __call__(self, resource: Resource, force: bool = False) -> None: ...
def process(self, source: StrOrBytesPath, target: StrOrBytesPath) -> Any: ...
def should_process(self, source: StrOrBytesPath, target: StrOrBytesPath) -> bool: ...
@property
@abstractmethod
def available(self) -> bool: ...
def source_path(self, resource: Resource) -> str | None: ...
def target_path(self, resource: Resource) -> str | None: ...
class Minifier(Compiler):
@property
@abstractmethod
def name(self) -> str: ...
@property
@abstractmethod
def source_extension(self) -> str: ...
@property
@abstractmethod
def target_extension(self) -> str: ...
def source_to_target(self, resource: Resource) -> str: ...
def compile_resources(argv: list[str] = ...) -> None: ...
class sdist_compile(setuptools.command.sdist.sdist): ...
class NullCompiler(Compiler):
name: ClassVar[Literal[""]]
source_extension = NotImplemented
def source_path(self, resource: Resource) -> None: ...
def target_path(self, resource: Resource) -> None: ...
def should_process(self, source: StrOrBytesPath, target: StrOrBytesPath) -> Literal[False]: ...
@property
def available(self) -> Literal[False]: ...
_SourceType = NewType("_SourceType", object)
_TargetType = NewType("_TargetType", object)
SOURCE: _SourceType
TARGET: _TargetType
class CommandlineBase:
@property
@abstractmethod
def command(self) -> str: ...
arguments: ClassVar[list[str]]
@property
def available(self) -> bool: ...
def process(self, source: StrOrBytesPath | _SourceType, target: StrOrBytesPath | _TargetType) -> Popen[str]: ...
class CoffeeScript(CommandlineBase, Compiler):
name: ClassVar[Literal["coffee"]]
command: ClassVar[Literal["coffee"]]
source_extension = NotImplemented
def process( # type:ignore[override]
self, source: StrOrBytesPath | _SourceType, target: StrOrBytesPath | _TargetType
) -> None: ...
COFFEE_COMPILER: CoffeeScript
class LESS(CommandlineBase, Compiler):
name: ClassVar[Literal["less"]]
command: ClassVar[Literal["lessc"]]
source_extension = NotImplemented
LESS_COMPILER: LESS
class SASS(CommandlineBase, Compiler):
name: ClassVar[Literal["sass"]]
command: ClassVar[Literal["sass"]]
source_extension: ClassVar[Literal[".scss"]]
SASS_COMPILER: SASS
class PythonPackageBase:
@property
@abstractmethod
def package(self) -> str: ...
@property
def available(self) -> bool: ...
class CSSMin(PythonPackageBase, Minifier):
name: ClassVar[Literal["cssmin"]]
package: ClassVar[Literal["cssmin"]]
source_extension = NotImplemented
target_extension: ClassVar[Literal[".min.css"]]
CSSMIN_MINIFIER: CSSMin
class JSMin(PythonPackageBase, Minifier):
name: ClassVar[Literal["jsmin"]]
package: ClassVar[Literal["jsmin"]]
source_extension = NotImplemented
target_extension: ClassVar[Literal[".min.js"]]
JSMIN_MINIFIER: JSMin
class Closure(PythonPackageBase, Minifier):
name: ClassVar[Literal["closure"]]
package: ClassVar[Literal["closure"]]
source_extension = NotImplemented
target_extension: Literal[".min.js"]
arguments: ClassVar[list[str]]
def process(self, source: StrOrBytesPath, target: StrOrBytesPath) -> Popen[str]: ...
CLOSURE_MINIFIER: Closure

View File

@@ -0,0 +1,10 @@
from _typeshed import SupportsItems
from typing import TypeVar
_KT = TypeVar("_KT")
_VT = TypeVar("_VT")
BOOL_CONFIG: set[str]
def asbool(obj: object) -> bool: ...
def convert_config(config: SupportsItems[_KT, _VT]) -> dict[_KT, _VT | bool]: ...

View File

@@ -0,0 +1,237 @@
from abc import abstractmethod
from collections.abc import Callable, Iterable
from threading import local
from types import ModuleType
from typing import NewType
from typing_extensions import Literal, TypeAlias
from fanstatic.compiler import Compiler, Minifier
_Renderer: TypeAlias = Callable[[str], str]
DEFAULT_SIGNATURE: str
VERSION_PREFIX: str
BUNDLE_PREFIX: str
NEEDED: str
DEBUG: str
MINIFIED: str
def set_resource_file_existence_checking(v: bool) -> None: ...
def set_auto_register_library(v: bool) -> None: ...
class UnknownResourceExtensionError(Exception): ...
class ModeResourceDependencyError(Exception): ...
UnknownResourceExtension = UnknownResourceExtensionError
class UnknownResourceError(Exception): ...
class ConfigurationError(Exception): ...
class LibraryDependencyCycleError(Exception): ...
class SlotError(Exception): ...
class Library:
path: str
name: str
rootpath: str
ignores: list[str]
version: str | None
known_resources: dict[str, Resource]
known_assets: dict[str, Asset]
module: ModuleType
compilers: dict[str, Compiler]
minifiers: dict[str, Minifier]
def __init__(
self,
name: str,
rootpath: str,
ignores: list[str] | None = None,
version: str | None = None,
compilers: dict[str, Compiler] | None = None,
minifiers: dict[str, Minifier] | None = None,
) -> None: ...
def check_dependency_cycle(self, resource: Resource) -> None: ...
def register(self, resource: Resource) -> None: ...
def signature(self, recompute_hashes: bool = False, version_method: Callable[[str], str] | None = None) -> str: ...
def caller_dir() -> str: ...
class InclusionRenderers(dict[str, tuple[int, _Renderer]]):
def register(self, extension: str, renderer: _Renderer, order: int | None = None) -> None: ...
inclusion_renderers: InclusionRenderers
def register_inclusion_renderer(extension: str, renderer: _Renderer, order: int | None = None) -> None: ...
def render_ico(url: str) -> str: ...
def render_css(url: str) -> str: ...
def render_js(url: str) -> str: ...
def render_print_css(url: str) -> str: ...
def render_screen_css(url: str) -> str: ...
class Renderable:
@abstractmethod
def render(self, library_url: str) -> str: ...
class Dependable:
@property
@abstractmethod
def resources(self) -> set[Dependable]: ...
@property
@abstractmethod
def depends(self) -> set[Dependable]: ...
@property
@abstractmethod
def supports(self) -> set[Dependable]: ...
def add_dependency(self, dependency: Dependable) -> None: ...
@abstractmethod
def set_dependencies(self, dependencies: Iterable[Dependable] | None) -> None: ...
@abstractmethod
def list_assets(self) -> set[Asset]: ...
def list_supporting(self) -> set[Dependable]: ...
class Asset(Dependable):
resources: set[Dependable]
depends: set[Dependable]
supports: set[Dependable]
library: Library
def __init__(self, library: Library, depends: Iterable[Dependable] | None = None) -> None: ...
def set_dependencies(self, depends: Iterable[Dependable] | None) -> None: ...
def list_assets(self) -> set[Asset]: ...
_NothingType = NewType("_NothingType", object)
NOTHING: _NothingType
class Resource(Renderable, Asset):
relpath: str
ext: str
mode_parent: str | None
compiler: Compiler
source: str | None
minifier: Minifier
minified: Resource | None
bottom: bool
dont_bundle: bool
renderer: _Renderer
modes: dict[str, Resource]
supersedes: list[Resource]
rollups: list[Resource]
def __init__(
self,
library: Library,
relpath: str,
depends: Iterable[Dependable] | None = None,
supersedes: list[Resource] | None = None,
bottom: bool = False,
renderer: _Renderer | None = None,
debug: str | Resource | None = None,
dont_bundle: bool = False,
minified: str | Resource | None = None,
minifier: Minifier | _NothingType = ...,
compiler: Compiler | _NothingType = ...,
source: str | None = None,
mode_parent: str | None = None,
) -> None: ...
def fullpath(self, path: str | None = None) -> str: ...
def compile(self, force: bool = False) -> None: ...
def render(self, library_url: str) -> str: ...
def mode(self, mode: str | None) -> Resource: ...
def need(self, slots: dict[Slot, Resource] | None = None) -> None: ...
_RequiredDefaultMarkerType = NewType("_RequiredDefaultMarkerType", object)
REQUIRED_DEFAULT_MARKER: _RequiredDefaultMarkerType
class Slot(Asset):
default: Resource | None
ext: str
required: bool
def __init__(
self,
library: Library,
extension: str,
depends: Iterable[Dependable] | None = None,
required: bool | _RequiredDefaultMarkerType = ...,
default: Resource | None = None,
) -> None: ...
class FilledSlot(Renderable):
filledby: Resource
library: Library
relpath: str
bottom: bool
rollups: list[Resource]
dont_bundle: bool
ext: str
order: int
renderer: _Renderer
dependency_nr: int
modes: dict[str, FilledSlot]
def __init__(self, slot: Slot, resource: Resource) -> None: ...
def render(self, library_url: str) -> str: ...
def compile(self, force: bool = False) -> None: ...
def mode(self, mode: str | None) -> FilledSlot: ...
class Group(Dependable):
resources: set[Dependable]
depends: set[Dependable]
supports: set[Dependable]
def __init__(self, depends: Iterable[Dependable]) -> None: ...
def set_dependencies(self, depends: Iterable[Dependable]) -> None: ... # type:ignore[override]
def list_assets(self) -> set[Asset]: ...
def need(self, slots: dict[Slot, Resource] | None = None) -> None: ...
GroupResource = Group
class NeededResources:
def __init__(
self,
versioning: bool = False,
versioning_use_md5: bool = False,
recompute_hashes: bool = True,
base_url: str | None = None,
script_name: str | None = None,
publisher_signature: str = ...,
resources: Iterable[Dependable] | None = None,
) -> None: ...
def has_resources(self) -> bool: ...
def has_base_url(self) -> bool: ...
def set_base_url(self, url: str) -> None: ...
def need(self, resource: Resource | Group, slots: dict[Slot, Resource] | None = None) -> None: ...
def resources(self) -> set[Resource]: ...
def clear(self) -> None: ...
def library_url(self, library: Library) -> str: ...
class DummyNeededResources:
def need(self, resource: Resource | Group, slots: dict[Slot, Resource] | None = None) -> None: ...
def has_resources(self) -> Literal[False]: ...
thread_local_needed_data: local
def init_needed(
versioning: bool = False,
versioning_use_md5: bool = False,
recompute_hashes: bool = True,
base_url: str | None = None,
script_name: str | None = None,
publisher_signature: str = ...,
resources: Iterable[Dependable] | None = None,
) -> NeededResources: ...
def del_needed() -> None: ...
def get_needed() -> NeededResources | DummyNeededResources: ...
def clear_needed() -> None: ...
class Bundle(Renderable):
def __init__(self) -> None: ...
@property
def dirname(self) -> str: ...
@property
def library(self) -> Library: ...
@property
def renderer(self) -> _Renderer: ...
@property
def ext(self) -> str: ...
@property
def relpath(self) -> str: ...
def resources(self) -> list[Resource]: ...
def render(self, library_url: str) -> str: ...
def fits(self, resource: Resource) -> bool: ...
def append(self, resource: Resource) -> None: ...
def add_to_list(self, result: list[Renderable]) -> None: ...

View File

@@ -0,0 +1,22 @@
from collections.abc import Iterable
from fanstatic.core import Bundle, NeededResources, Resource
def bundle_resources(resources: Iterable[Resource]) -> list[Resource | Bundle]: ...
def rollup_resources(resources: Iterable[Resource]) -> set[Resource]: ...
def sort_resources(resources: Iterable[Resource]) -> list[Resource]: ...
class Inclusion:
needed: NeededResources
resources: list[Resource | Bundle]
def __init__(
self,
needed: NeededResources,
resources: Iterable[Resource] | None = None,
compile: bool = False,
bundle: bool = False,
mode: str | None = None,
rollup: bool = False,
) -> None: ...
def __len__(self) -> int: ...
def render(self) -> str: ...

View File

@@ -0,0 +1,57 @@
from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
from abc import abstractmethod
from collections.abc import Iterable
from typing import Any
from typing_extensions import Literal, TypedDict
from fanstatic.core import Dependable, NeededResources, Resource
from fanstatic.inclusion import Inclusion
from webob import Request, Response
class _NeededResourcesConfig(TypedDict, total=False):
versioning: bool
versioning_use_md5: bool
recompute_hashes: bool
base_url: str | None
script_name: str | None
publisher_signature: str
resources: Iterable[Dependable] | None
class _InjectorPluginOptions(TypedDict, total=False):
compile: bool
bundle: bool
rollup: bool
debug: bool
minified: bool
class _TopBottomInjectorPluginOptions(_InjectorPluginOptions, total=False):
bottom: bool
force_bottom: bool
CONTENT_TYPES: list[str]
class Injector:
app: WSGIApplication
config: _NeededResourcesConfig
injector: InjectorPlugin
def __init__(
self, app: WSGIApplication, injector: InjectorPlugin | None = None, **config: Any
) -> None: ... # FIXME: Switch to Unpack[_NeededResourcesConfig]
def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ...
class InjectorPlugin:
@property
@abstractmethod
def name(self) -> str: ...
def __init__(self, options: _InjectorPluginOptions) -> None: ...
def make_inclusion(self, needed: NeededResources, resources: set[Resource] | None = None) -> Inclusion: ...
def __call__(
self, html: bytes, needed: NeededResources, request: Request | None = None, response: Response | None = None
) -> None: ...
class TopBottomInjector(InjectorPlugin):
name: Literal["topbottom"]
def __init__(self, options: _TopBottomInjectorPluginOptions) -> None: ...
def group(self, needed: NeededResources) -> tuple[Inclusion, Inclusion]: ...
def make_injector(app: WSGIApplication, global_config: Any, **local_config: Any) -> Injector: ...

View File

@@ -0,0 +1,49 @@
from _typeshed import StrOrBytesPath
from _typeshed.wsgi import StartResponse, WSGIApplication, WSGIEnvironment
from collections.abc import Iterable
from typing import IO, Any
from typing_extensions import Literal
from fanstatic.core import Library
from fanstatic.registry import LibraryRegistry
from webob import Request, Response
from webob.dec import wsgify
from webob.static import DirectoryApp, FileApp
MINUTE_IN_SECONDS: Literal[60]
HOUR_IN_SECONDS: Literal[3600]
DAY_IN_SECONDS: Literal[86400]
YEAR_IN_SECONDS: int
FOREVER: int
class BundleApp(FileApp):
filenames: list[str]
def __init__(self, rootpath: str, bundle: IO[bytes], filenames: Iterable[StrOrBytesPath]) -> None: ...
@wsgify
def __call__(self, req: Request) -> Response: ...
class LibraryPublisher(DirectoryApp):
ignores: list[str]
library: Library
cached_apps: dict[str, FileApp]
def __init__(self, library: Library) -> None: ...
@wsgify
def __call__(self, req: Request) -> Response: ...
class Publisher:
registry: LibraryRegistry
directory_publishers: dict[str, LibraryPublisher]
def __init__(self, registry: LibraryRegistry) -> None: ...
@wsgify
def __call__(self, request: Request) -> Response: ...
class Delegator:
app: WSGIApplication
publisher: Publisher
publisher_signature: str
trigger: str
def __init__(self, app: WSGIApplication, publisher: Publisher, publisher_signature: str = ...) -> None: ...
def is_resource(self, request: Request) -> bool: ...
def __call__(self, environ: WSGIEnvironment, start_response: StartResponse) -> Iterable[bytes]: ...
def make_publisher(global_config: Any) -> Publisher: ...

View File

@@ -0,0 +1,45 @@
from abc import abstractmethod
from collections.abc import Iterable
from threading import Lock
from typing import Any, ClassVar, Protocol, TypeVar
from typing_extensions import Literal, Self
from fanstatic.compiler import Compiler, Minifier
from fanstatic.core import Library
from fanstatic.injector import InjectorPlugin
from pkg_resources import EntryPoint
class _HasName(Protocol):
@property
def name(self) -> str: ...
_NamedT = TypeVar("_NamedT", bound=_HasName)
prepare_lock: Lock
class Registry(dict[str, _NamedT]):
@property
@abstractmethod
def ENTRY_POINT(self) -> str: ...
def __init__(self, items: Iterable[_NamedT] = ()) -> None: ... # noqaY011
def add(self, item: _NamedT) -> None: ...
def load_items_from_entry_points(self) -> None: ...
def make_item_from_entry_point(self, entry_point: EntryPoint) -> Any: ...
@classmethod
def instance(cls) -> Self: ...
class LibraryRegistry(Registry[Library]):
ENTRY_POINT: ClassVar[Literal["fanstatic.libraries"]]
prepared: bool
def prepare(self) -> None: ...
get_library_registry = LibraryRegistry.instance
class CompilerRegistry(Registry[Compiler]):
ENTRY_POINT: ClassVar[Literal["fanstatic.compilers"]]
class MinifierRegistry(Registry[Minifier]):
ENTRY_POINT: ClassVar[Literal["fanstatic.minifiers"]]
class InjectorRegistry(Registry[InjectorPlugin]):
ENTRY_POINT: ClassVar[Literal["fanstatic.injectors"]]

View File

@@ -0,0 +1,22 @@
from _typeshed.wsgi import WSGIApplication
from typing import Any
from fanstatic.core import Resource
from fanstatic.injector import InjectorPlugin
from fanstatic.publisher import Delegator
from webob import Request, Response
from webob.dec import wsgify
def Fanstatic(
app: WSGIApplication, publisher_signature: str = ..., injector: InjectorPlugin | None = None, **config: Any
) -> Delegator: ...
def make_fanstatic(app: WSGIApplication, global_config: Any, **local_config: Any) -> Delegator: ...
class Serf:
resource: Resource
def __init__(self, resource: Resource) -> None: ...
@wsgify
def __call__(self, request: Request) -> Response: ...
def make_serf(global_config: Any, **local_config: Any) -> Serf: ...
def resolve(name: str, module: str | None = None) -> Any: ...