Skip to content

Commit

Permalink
Add stubs for fanstatic (#9931)
Browse files Browse the repository at this point in the history
  • Loading branch information
Daverball authored Nov 21, 2023
1 parent a9fc14a commit e6c3219
Show file tree
Hide file tree
Showing 12 changed files with 700 additions and 0 deletions.
77 changes: 77 additions & 0 deletions stubs/fanstatic/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -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.*
3 changes: 3 additions & 0 deletions stubs/fanstatic/METADATA.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
version = "1.4.*"
upstream_repository = "https://github.com/zopefoundation/fanstatic"
requires = ["types-setuptools", "types-WebOb"]
43 changes: 43 additions & 0 deletions stubs/fanstatic/fanstatic/__init__.pyi
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions stubs/fanstatic/fanstatic/checksum.pyi
Original file line number Diff line number Diff line change
@@ -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: ...
125 changes: 125 additions & 0 deletions stubs/fanstatic/fanstatic/compiler.pyi
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions stubs/fanstatic/fanstatic/config.pyi
Original file line number Diff line number Diff line change
@@ -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]: ...
Loading

0 comments on commit e6c3219

Please sign in to comment.