Typing: add stubs, hints and tooling settings
This commit is contained in:
parent
ba98f0c389
commit
e5cfceda3a
1
Pipfile
1
Pipfile
|
@ -22,6 +22,7 @@ mypy = "*"
|
|||
isort = "*"
|
||||
types-markdown = "*"
|
||||
types-pyyaml = "*"
|
||||
mypy-zope = "*"
|
||||
|
||||
[pipenv]
|
||||
allow_prereleases = true
|
||||
|
|
139
Pipfile.lock
generated
139
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "7a1e05f686d4492800c29d951c4ec9c8fa9e37b01bffa4acc80a28de398ea4c7"
|
||||
"sha256": "1e70e3d45c60ddbd24fc61b5f1a2fbb8c8fef62391c7af2e89893a560c1831fc"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -44,6 +44,7 @@
|
|||
"sha256:12611c4b0a8b1c461646228344784a1089bc0c49975680a2f54f516e71e9b79e",
|
||||
"sha256:12f40f78dcba4aa7d1354d35acf45fae9488862a4fb695c7eeda5ace6aae273f",
|
||||
"sha256:14d41933510717f98aac63378b7956bbe548986e435df173c841d7f2bd0b2de7",
|
||||
"sha256:196008d91201bbb1aa4e666fee5e610face25d532e433a560cabb33bfdff958b",
|
||||
"sha256:24c2ebd287b5b11016f31d506ca1052d068c3f9dc817160628504690376ff050",
|
||||
"sha256:2ade10e8613a3b8446214846d3ddbd56cfe9205a7d64742f0b75458c868f7492",
|
||||
"sha256:2e197534c884336f9020c1f3a8efbaab0aa96fc798068cb2da9c671818b7fbb0",
|
||||
|
@ -172,11 +173,11 @@
|
|||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb",
|
||||
"sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"
|
||||
"sha256:7fc841f8b8332803464e5dc1c63a2e59121f46ca186c0e2e182e80bf8c1319f7",
|
||||
"sha256:d97503976bb81f40a193d41ee6570868479c69d5068651eb039c40d850c59d67"
|
||||
],
|
||||
"markers": "python_version < '3.10'",
|
||||
"version": "==6.8.0"
|
||||
"version": "==7.0.0"
|
||||
},
|
||||
"incremental": {
|
||||
"hashes": [
|
||||
|
@ -538,37 +539,37 @@
|
|||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:12cce78e329838d70a204293e7b29af9faa3ab14899aec397798a4b41be7f340",
|
||||
"sha256:1484b8fa2c10adf4474f016e09d7a159602f3239075c7bf9f1627f5acf40ad49",
|
||||
"sha256:204e0d6de5fd2317394a4eff62065614c4892d5a4d1a7ee55b765d7a3d9e3f82",
|
||||
"sha256:2643d145af5292ee956aa0a83c2ce1038a3bdb26e033dadeb2f7066fb0c9abce",
|
||||
"sha256:2c6e4464ed5f01dc44dc9821caf67b60a4e5c3b04278286a85c067010653a0eb",
|
||||
"sha256:2f7f6985d05a4e3ce8255396df363046c28bea790e40617654e91ed580ca7c51",
|
||||
"sha256:31902408f4bf54108bbfb2e35369877c01c95adc6192958684473658c322c8a5",
|
||||
"sha256:40716d1f821b89838589e5b3106ebbc23636ffdef5abc31f7cd0266db936067e",
|
||||
"sha256:4b901927f16224d0d143b925ce9a4e6b3a758010673eeded9b748f250cf4e8f7",
|
||||
"sha256:4fc3d14ee80cd22367caaaf6e014494415bf440980a3045bf5045b525680ac33",
|
||||
"sha256:5cf3f0c5ac72139797953bd50bc6c95ac13075e62dbfcc923571180bebb662e9",
|
||||
"sha256:6dbdec441c60699288adf051f51a5d512b0d818526d1dcfff5a41f8cd8b4aaf1",
|
||||
"sha256:72cf32ce7dd3562373f78bd751f73c96cfb441de147cc2448a92c1a308bd0ca6",
|
||||
"sha256:75aa828610b67462ffe3057d4d8a4112105ed211596b750b53cbfe182f44777a",
|
||||
"sha256:75c4d2a6effd015786c87774e04331b6da863fc3fc4e8adfc3b40aa55ab516fe",
|
||||
"sha256:78e25b2fd6cbb55ddfb8058417df193f0129cad5f4ee75d1502248e588d9e0d7",
|
||||
"sha256:84860e06ba363d9c0eeabd45ac0fde4b903ad7aa4f93cd8b648385a888e23200",
|
||||
"sha256:8c5091ebd294f7628eb25ea554852a52058ac81472c921150e3a61cdd68f75a7",
|
||||
"sha256:944bdc21ebd620eafefc090cdf83158393ec2b1391578359776c00de00e8907a",
|
||||
"sha256:9c7ac372232c928fff0645d85f273a726970c014749b924ce5710d7d89763a28",
|
||||
"sha256:d9b338c19fa2412f76e17525c1b4f2c687a55b156320acb588df79f2e6fa9fea",
|
||||
"sha256:ee5d62d28b854eb61889cde4e1dbc10fbaa5560cb39780c3995f6737f7e82120",
|
||||
"sha256:f2c2521a8e4d6d769e3234350ba7b65ff5d527137cdcde13ff4d99114b0c8e7d",
|
||||
"sha256:f6efc9bd72258f89a3816e3a98c09d36f079c223aa345c659622f056b760ab42",
|
||||
"sha256:f7c5d642db47376a0cc130f0de6d055056e010debdaf0707cd2b0fc7e7ef30ea",
|
||||
"sha256:fcb6d9afb1b6208b4c712af0dafdc650f518836065df0d4fb1d800f5d6773db2",
|
||||
"sha256:fcd2572dd4519e8a6642b733cd3a8cfc1ef94bafd0c1ceed9c94fe736cb65b6a"
|
||||
"sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315",
|
||||
"sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0",
|
||||
"sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373",
|
||||
"sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a",
|
||||
"sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161",
|
||||
"sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275",
|
||||
"sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693",
|
||||
"sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb",
|
||||
"sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65",
|
||||
"sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4",
|
||||
"sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb",
|
||||
"sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243",
|
||||
"sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14",
|
||||
"sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4",
|
||||
"sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1",
|
||||
"sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a",
|
||||
"sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160",
|
||||
"sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25",
|
||||
"sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12",
|
||||
"sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d",
|
||||
"sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92",
|
||||
"sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770",
|
||||
"sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2",
|
||||
"sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70",
|
||||
"sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb",
|
||||
"sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5",
|
||||
"sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.7.1"
|
||||
"version": "==1.5.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
|
@ -578,6 +579,14 @@
|
|||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"mypy-zope": {
|
||||
"hashes": [
|
||||
"sha256:003953896629d762d7f497135171ad549df42a8ac63c1521a230832dd6f7fc25",
|
||||
"sha256:ffa291a7af9f5904ce9f0e56de44323a4476e28aaf0d68361b62b1b0e997d0b8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
"sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda",
|
||||
|
@ -675,6 +684,14 @@
|
|||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==13.7.0"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2",
|
||||
"sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==69.0.2"
|
||||
},
|
||||
"smmap": {
|
||||
"hashes": [
|
||||
"sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62",
|
||||
|
@ -723,6 +740,64 @@
|
|||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==4.9.0rc1"
|
||||
},
|
||||
"zope.event": {
|
||||
"hashes": [
|
||||
"sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26",
|
||||
"sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==5.0"
|
||||
},
|
||||
"zope.interface": {
|
||||
"hashes": [
|
||||
"sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff",
|
||||
"sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c",
|
||||
"sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac",
|
||||
"sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f",
|
||||
"sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d",
|
||||
"sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309",
|
||||
"sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736",
|
||||
"sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179",
|
||||
"sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb",
|
||||
"sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941",
|
||||
"sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d",
|
||||
"sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92",
|
||||
"sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b",
|
||||
"sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41",
|
||||
"sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f",
|
||||
"sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3",
|
||||
"sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d",
|
||||
"sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8",
|
||||
"sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3",
|
||||
"sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1",
|
||||
"sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1",
|
||||
"sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40",
|
||||
"sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d",
|
||||
"sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1",
|
||||
"sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605",
|
||||
"sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7",
|
||||
"sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd",
|
||||
"sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43",
|
||||
"sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0",
|
||||
"sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b",
|
||||
"sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379",
|
||||
"sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a",
|
||||
"sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83",
|
||||
"sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56",
|
||||
"sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9",
|
||||
"sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.1"
|
||||
},
|
||||
"zope.schema": {
|
||||
"hashes": [
|
||||
"sha256:cf006c678793b00e0075ad54d55281c8785ea21e5bc1f5ec0584787719c2aab2",
|
||||
"sha256:ead4dbcb03354d4e410c9a3b904451eb44d90254751b1cbdedf4a61aede9fbb9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==7.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
mypy.ini
Normal file
14
mypy.ini
Normal file
|
@ -0,0 +1,14 @@
|
|||
[mypy]
|
||||
namespace_packages=True
|
||||
plugins=mypy_zope:plugin
|
||||
#TODO: progress so we can enable this
|
||||
#strict=True
|
||||
check_untyped_defs=True
|
||||
disallow_untyped_defs=True
|
||||
warn_redundant_casts=True
|
||||
warn_unused_configs= True
|
||||
warn_unused_ignores = False
|
||||
warn_no_return=True
|
||||
warn_return_any=True
|
||||
warn_unreachable=True
|
||||
enable_error_code=unused-awaitable
|
17
pyproject.toml
Normal file
17
pyproject.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[tool.black]
|
||||
target-version = ['py39']
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
sections = "FUTURE,STDLIB,ZOPE,OPENSSL,THIRDPARTY,FIRSTPARTY,LOCALFOLDER"
|
||||
default_section = "THIRDPARTY"
|
||||
no_lines_before = "LOCALFOLDER"
|
||||
known_first_party = "adlermanager"
|
||||
known_zope = "zope"
|
||||
known_openssl = "OpenSSL"
|
||||
combine_as_imports = true
|
||||
#skip = "src/twisted/logger/__init__.py,src/twisted/internet/reactor.py"
|
||||
|
||||
[tool.pyright]
|
||||
include = ["src"]
|
||||
reportIncompatibleMethodOverride = false
|
9
setup.cfg
Normal file
9
setup.cfg
Normal file
|
@ -0,0 +1,9 @@
|
|||
[flake8]
|
||||
profile = "black"
|
||||
max-line-length = 88
|
||||
extend-ignore = E203
|
||||
statistics = True
|
||||
|
||||
[options]
|
||||
package_dir = =src
|
||||
packages = find:
|
|
@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
|||
class AdlerManagerSSHProtocol(SSHSimpleProtocol):
|
||||
sites_manager: "SitesManager"
|
||||
|
||||
def __init__(self, user: SSHSimpleAvatar, interactive: bool = True):
|
||||
def __init__(self, user: SSHSimpleAvatar, interactive: bool = True) -> None:
|
||||
"""
|
||||
Create an instance of AdlerManagerSSHProtocol.
|
||||
"""
|
||||
|
@ -19,7 +19,7 @@ class AdlerManagerSSHProtocol(SSHSimpleProtocol):
|
|||
|
||||
# TODO: Do stuff like getting user sites, showing alert warnings, etc.
|
||||
|
||||
def do_tmp_dump_state(self):
|
||||
def do_tmp_dump_state(self) -> None:
|
||||
"""
|
||||
This command is temporary and just dumps all known state.
|
||||
"""
|
||||
|
@ -32,6 +32,6 @@ class AdlerManagerSSHProtocol(SSHSimpleProtocol):
|
|||
self.terminal.nextLine()
|
||||
|
||||
@functools.lru_cache() # we don't need to re-read every time
|
||||
def motd(self):
|
||||
def motd(self) -> str:
|
||||
# TODO: Use data location?
|
||||
return open("motd.txt").read()
|
||||
|
|
|
@ -25,7 +25,7 @@ class AdlerManagerTokenResource(TokenResource):
|
|||
"""
|
||||
TokenResource.__init__(self, tokens=sites_manager.tokens)
|
||||
|
||||
def preprocess_header(self, header: str):
|
||||
def preprocess_header(self, header: str) -> str:
|
||||
return header.split(" ")[-1]
|
||||
|
||||
def processToken(self, token_data: "SiteManager", request: Request) -> int:
|
||||
|
|
|
@ -22,7 +22,7 @@ class IncidentManager(object):
|
|||
_alert_timeouts: Dict[str, defer.Deferred[None]] = attr.ib(factory=dict)
|
||||
"""alert_label -> timeout"""
|
||||
|
||||
_monitoring_down = attr.ib(default=False)
|
||||
_monitoring_down: bool = attr.ib(default=False)
|
||||
|
||||
# _logs = attr.ib(factory=list)
|
||||
# TODO: Get IncidentClosing timeout from settings?
|
||||
|
@ -31,7 +31,7 @@ class IncidentManager(object):
|
|||
# Defaulting to 5m as alertmanager
|
||||
_alert_resolve_timeout: int = attr.ib(default=5 * 60)
|
||||
|
||||
def process_heartbeats(self, heartbeats: Iterable[Alert], timestamp: str):
|
||||
def process_heartbeats(self, heartbeats: Iterable[Alert], timestamp: str) -> None:
|
||||
if heartbeats:
|
||||
self.last_updated = timestamp
|
||||
if self._monitoring_down:
|
||||
|
@ -42,7 +42,7 @@ class IncidentManager(object):
|
|||
)
|
||||
self.log_event("[Meta]MonitoringUp", timestamp)
|
||||
|
||||
def process_alerts(self, alerts: Iterable[Alert], timestamp: str):
|
||||
def process_alerts(self, alerts: Iterable[Alert], timestamp: str) -> None:
|
||||
if alerts:
|
||||
self._timeout.cancel()
|
||||
self._timeout = task.deferLater(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict, Generator, List, cast
|
||||
from typing import Any, Dict, Generator, List, Optional, cast
|
||||
|
||||
import attr
|
||||
import yaml
|
||||
|
@ -38,17 +38,17 @@ class SiteManager(object):
|
|||
path: FilePath = attr.ib()
|
||||
tokens: List[str] = attr.ib(factory=list)
|
||||
log = Logger()
|
||||
monitoring_is_down = attr.ib(default=False)
|
||||
monitoring_is_down: bool = attr.ib(default=False)
|
||||
definition: Dict[str, Any] = attr.ib(factory=dict)
|
||||
title = attr.ib(default="")
|
||||
title: str = attr.ib(default="")
|
||||
|
||||
_timeout: defer.Deferred[None] = attr.ib(factory=noop_deferred)
|
||||
site_name = attr.ib(default="")
|
||||
site_name: str = attr.ib(default="")
|
||||
# TODO: Get monitoring timeout from config
|
||||
# Default to 2 mins
|
||||
_timeout_seconds = 2 * 60
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
def __attrs_post_init__(self) -> None:
|
||||
self.load_definition()
|
||||
self.title = self.definition["title"]
|
||||
self.load_tokens()
|
||||
|
@ -67,11 +67,11 @@ class SiteManager(object):
|
|||
for manager in self.service_managers:
|
||||
manager.monitoring_down(self.last_updated.getStr())
|
||||
|
||||
def load_definition(self):
|
||||
def load_definition(self) -> None:
|
||||
with self.path.child("site.yml").open("r") as f:
|
||||
self.definition = yaml.safe_load(f)
|
||||
|
||||
def load_tokens(self):
|
||||
def load_tokens(self) -> None:
|
||||
tokens_file = self.path.child("tokens.txt")
|
||||
if tokens_file.exists():
|
||||
with open(tokens_file.path, "r") as f:
|
||||
|
@ -82,7 +82,7 @@ class SiteManager(object):
|
|||
"your site will never update".format(self.title)
|
||||
)
|
||||
|
||||
def process_alerts(self, raw_alerts: List[Dict[str, Any]]):
|
||||
def process_alerts(self, raw_alerts: List[Dict[str, Any]]) -> None:
|
||||
self.last_updated.now()
|
||||
|
||||
self.monitoring_is_down = False
|
||||
|
@ -112,7 +112,7 @@ class SiteManager(object):
|
|||
manager.process_alerts(filtered_alerts, timestamp)
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
def status(self) -> Severity:
|
||||
if self.monitoring_is_down:
|
||||
return Severity.ERROR
|
||||
return max(
|
||||
|
@ -124,13 +124,13 @@ class SiteManager(object):
|
|||
class ServiceManager(object):
|
||||
path: FilePath = attr.ib()
|
||||
definition: Dict[str, Any] = attr.ib()
|
||||
current_incident = attr.ib(default=None)
|
||||
current_incident: Optional[IncidentManager] = attr.ib(default=None)
|
||||
component_labels: List[str] = attr.ib(factory=list)
|
||||
label = attr.ib(default="")
|
||||
label: str = attr.ib(default="")
|
||||
|
||||
log = Logger()
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
def __attrs_post_init__(self) -> None:
|
||||
self.label = self.definition["label"]
|
||||
# TODO: Recover status after server restart
|
||||
self.component_labels = [
|
||||
|
@ -144,11 +144,11 @@ class ServiceManager(object):
|
|||
if self.current_incident:
|
||||
self.current_incident.monitoring_down(timestamp)
|
||||
|
||||
def process_heartbeats(self, heartbeats: List[Alert], timestamp: str):
|
||||
def process_heartbeats(self, heartbeats: List[Alert], timestamp: str) -> None:
|
||||
if self.current_incident:
|
||||
self.current_incident.process_heartbeats(heartbeats, timestamp)
|
||||
|
||||
def process_alerts(self, alerts: List[Alert], timestamp: str):
|
||||
def process_alerts(self, alerts: List[Alert], timestamp: str) -> None:
|
||||
# Filter by service-affecting alerts
|
||||
alerts = [
|
||||
alert
|
||||
|
@ -172,7 +172,7 @@ class ServiceManager(object):
|
|||
self.current_incident = None
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
def status(self) -> Severity:
|
||||
if self.current_incident:
|
||||
# TODO: Consistent naming
|
||||
return max(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Generator, Union, cast
|
||||
|
||||
from twisted.internet import defer
|
||||
from twisted.web import resource, server
|
||||
|
@ -26,7 +26,7 @@ class TokenResource(resource.Resource):
|
|||
resource.Resource.__init__(self)
|
||||
self.tokens = tokens
|
||||
|
||||
def render(self, request: Request):
|
||||
def render(self, request: Request) -> Union[bytes, int]:
|
||||
"""
|
||||
See L{resource.Resource}.
|
||||
|
||||
|
@ -41,7 +41,9 @@ class TokenResource(resource.Resource):
|
|||
if token_data is None:
|
||||
return self._unauthorized(request)
|
||||
|
||||
self._processToken(token_data, request).addCallback(lambda x: request.finish())
|
||||
self._processToken(token_data, request).addCallback( # type: ignore
|
||||
lambda x: request.finish()
|
||||
)
|
||||
return server.NOT_DONE_YET
|
||||
|
||||
def preprocess_header(self, header: str) -> str:
|
||||
|
@ -63,7 +65,9 @@ class TokenResource(resource.Resource):
|
|||
return OK
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def _processToken(self, token_data: Any, request: Request):
|
||||
def _processToken(
|
||||
self, token_data: Any, request: Request
|
||||
) -> Generator[defer.Deferred[int], None, None]:
|
||||
"""
|
||||
Invoke L{TokenResource.processToken} and produce a 500 if it fails.
|
||||
|
||||
|
@ -71,9 +75,8 @@ class TokenResource(resource.Resource):
|
|||
@param request: The request object associated to this request.
|
||||
"""
|
||||
try:
|
||||
code: int = yield defer.maybeDeferred(
|
||||
self.processToken, token_data, request
|
||||
)
|
||||
res = yield defer.maybeDeferred(self.processToken, token_data, request)
|
||||
code: int = cast(int, res)
|
||||
except Exception:
|
||||
import traceback
|
||||
|
||||
|
@ -82,7 +85,7 @@ class TokenResource(resource.Resource):
|
|||
request.setResponseCode(code) # type: ignore
|
||||
defer.returnValue(code)
|
||||
|
||||
def _unauthorized(self, request: Request):
|
||||
def _unauthorized(self, request: Request) -> bytes:
|
||||
"""
|
||||
Send a 401 Unauthorized response.
|
||||
|
||||
|
@ -91,7 +94,7 @@ class TokenResource(resource.Resource):
|
|||
request.setResponseCode(UNAUTHORIZED) # type: ignore
|
||||
return self.unauthorizedPage().render(request) # type: ignore
|
||||
|
||||
def unauthorizedPage(self):
|
||||
def unauthorizedPage(self) -> resource.ErrorPage:
|
||||
"""
|
||||
Page to render when there is no valid token.
|
||||
This makes use of L{TokenResource.unauthorizedMessage} by default.
|
||||
|
@ -100,13 +103,13 @@ class TokenResource(resource.Resource):
|
|||
UNAUTHORIZED, "Unauthorized", self.unauthorizedMessage()
|
||||
)
|
||||
|
||||
def unauthorizedMessage(self):
|
||||
def unauthorizedMessage(self) -> str:
|
||||
"""
|
||||
Message to show when there is no valid token.
|
||||
"""
|
||||
return "Pass a valid token in the {} header.".format(self.HEADER)
|
||||
|
||||
def getChild(self, name: str, request: Request):
|
||||
def getChild(self, name: str, request: Request) -> "TokenResource":
|
||||
"""
|
||||
Use this child for everything but the explicitly overriden.
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import cast
|
|||
import jinja2
|
||||
import markdown
|
||||
from klein import Klein
|
||||
from klein.resource import KleinResource
|
||||
from twisted.logger import Logger
|
||||
from twisted.python.filepath import FilePath
|
||||
from twisted.web import resource, static
|
||||
|
@ -14,7 +15,7 @@ from .Config import Config
|
|||
from .SitesManager import SiteManager, SitesManager
|
||||
|
||||
|
||||
def get_jinja_env(supportDir: str):
|
||||
def get_jinja_env(supportDir: str) -> jinja2.Environment:
|
||||
"""
|
||||
Return a L{jinja2.Environment} with templates loaded from:
|
||||
- Package
|
||||
|
@ -46,7 +47,7 @@ def get_jinja_env(supportDir: str):
|
|||
return templates
|
||||
|
||||
|
||||
def web_root(sites_manager: "SitesManager"):
|
||||
def web_root(sites_manager: "SitesManager") -> KleinResource:
|
||||
app = Klein()
|
||||
log = Logger()
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Callable, Dict, Optional, Tuple, Union, cast
|
||||
from typing import Any, Callable, Dict, Iterator, Optional, Tuple, Union, cast
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
|
@ -14,6 +14,7 @@ from twisted.conch.insults import insults
|
|||
from twisted.conch.manhole_tap import chainedProtocolFactory
|
||||
from twisted.conch.ssh import keys, session
|
||||
from twisted.cred import portal
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.internet.error import ProcessTerminated
|
||||
from twisted.python import failure, filepath
|
||||
|
||||
|
@ -23,7 +24,7 @@ class SSHSimpleProtocol(recvline.HistoricRecvLine):
|
|||
keyHandlers: Dict[bytes, Callable[[], None]]
|
||||
interactive: bool = True
|
||||
|
||||
def __init__(self, user: "SSHSimpleAvatar", interactive: bool = True):
|
||||
def __init__(self, user: "SSHSimpleAvatar", interactive: bool = True) -> None:
|
||||
recvline.HistoricRecvLine.__init__(self)
|
||||
self.user = user
|
||||
self.interactive = interactive
|
||||
|
@ -35,7 +36,7 @@ class SSHSimpleProtocol(recvline.HistoricRecvLine):
|
|||
"""
|
||||
return self.terminal.write(msg) # type: ignore
|
||||
|
||||
def connectionMade(self):
|
||||
def connectionMade(self) -> None:
|
||||
recvline.HistoricRecvLine.connectionMade(self)
|
||||
if not self.interactive:
|
||||
return
|
||||
|
@ -49,16 +50,16 @@ class SSHSimpleProtocol(recvline.HistoricRecvLine):
|
|||
|
||||
self.showPrompt()
|
||||
|
||||
def handle_EOF(self):
|
||||
def handle_EOF(self) -> None:
|
||||
if self.lineBuffer: # type: ignore
|
||||
self.terminal_write(b"\a")
|
||||
else:
|
||||
self.exitWithCode(0)
|
||||
|
||||
def handle_QUIT(self):
|
||||
def handle_QUIT(self) -> None:
|
||||
self.terminal.loseConnection()
|
||||
|
||||
def showPrompt(self):
|
||||
def showPrompt(self) -> None:
|
||||
if self.interactive:
|
||||
self.terminal_write(">>> ")
|
||||
|
||||
|
@ -109,9 +110,9 @@ class SSHSimpleProtocol(recvline.HistoricRecvLine):
|
|||
failure.Failure(ProcessTerminated(code, None, None))
|
||||
)
|
||||
|
||||
def do_help(self, cmd: bytes = b""):
|
||||
def do_help(self, cmd: bytes = b"") -> None:
|
||||
"""
|
||||
Get help on a command. Usage: help command
|
||||
Get help on a command. Usage: help [command]
|
||||
"""
|
||||
if cmd:
|
||||
func = self._getCommand(cmd)
|
||||
|
@ -130,26 +131,26 @@ class SSHSimpleProtocol(recvline.HistoricRecvLine):
|
|||
self.terminal.nextLine()
|
||||
self.terminal.nextLine()
|
||||
|
||||
def do_whoami(self):
|
||||
def do_whoami(self) -> None:
|
||||
"""
|
||||
Prints your username. Usage: whoami
|
||||
"""
|
||||
self.terminal_write(self.user.username)
|
||||
self.terminal.nextLine()
|
||||
|
||||
def do_clear(self):
|
||||
def do_clear(self) -> None:
|
||||
"""
|
||||
Clears the screen. Usage: clear
|
||||
"""
|
||||
self.terminal.reset()
|
||||
|
||||
def do_exit(self):
|
||||
def do_exit(self) -> None:
|
||||
"""
|
||||
Exit session. Usage: exit
|
||||
"""
|
||||
self.exitWithCode(0)
|
||||
|
||||
def motd(self):
|
||||
def motd(self) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
|
@ -162,12 +163,12 @@ class SSHSimpleAvatar(avatar.ConchUser):
|
|||
self.proto = proto
|
||||
self.channelLookup.update({b"session": session.SSHSession}) # type: ignore
|
||||
|
||||
def openShell(self, protocol: session.SSHSessionProcessProtocol):
|
||||
def openShell(self, protocol: session.SSHSessionProcessProtocol) -> None:
|
||||
serverProtocol = insults.ServerProtocol(self.proto, self)
|
||||
serverProtocol.makeConnection(protocol) # type: ignore
|
||||
protocol.makeConnection(session.wrapProtocol(serverProtocol)) # type: ignore
|
||||
|
||||
def getPty(self, terminal, windowSize, attrs): # type: ignore
|
||||
def getPty(self, terminal, windowSize, attrs) -> None: # type: ignore
|
||||
return None
|
||||
|
||||
def execCommand(
|
||||
|
@ -188,7 +189,16 @@ class SSHSimpleAvatar(avatar.ConchUser):
|
|||
"""
|
||||
pass
|
||||
|
||||
def closed(self):
|
||||
def eofReceived(self) -> None:
|
||||
"""
|
||||
Called when the other side has indicated no more data will be sent.
|
||||
"""
|
||||
pass
|
||||
|
||||
def closed(self) -> None:
|
||||
"""
|
||||
Called when the session is closed.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
@ -211,14 +221,19 @@ class SSHSimpleRealm:
|
|||
"""
|
||||
self.proto = proto
|
||||
|
||||
def requestAvatar(self, avatarId: bytes, mind: Any, *interfaces: Any):
|
||||
def requestAvatar(
|
||||
self,
|
||||
avatarId: Union[bytes, Tuple[()]],
|
||||
mind: object,
|
||||
*interfaces: portal._InterfaceItself, # type: ignore
|
||||
) -> Union[Deferred[portal._requestResult], portal._requestResult]: # type: ignore
|
||||
"""
|
||||
Return a L{SSHSimpleAvatar} that uses ``self.proto`` as protocol.
|
||||
|
||||
@see: L{portal.IRealm}
|
||||
"""
|
||||
if conchinterfaces.IConchUser in interfaces:
|
||||
avatar = SSHSimpleAvatar(avatarId, self.proto)
|
||||
avatar = SSHSimpleAvatar(cast(bytes, avatarId), self.proto)
|
||||
return interfaces[0], avatar, lambda: None
|
||||
else:
|
||||
raise Exception("No supported interfaces found.")
|
||||
|
@ -251,7 +266,7 @@ class SSHKeyDirectory(object):
|
|||
self.baseDir = baseDir
|
||||
self.parseKey = parseKey
|
||||
|
||||
def getAuthorizedKeys(self, username: bytes):
|
||||
def getAuthorizedKeys(self, username: bytes) -> Iterator[keys.Key]:
|
||||
keyFile = self.baseDir.child(username + b".key")
|
||||
keyDir = self.baseDir.child(username)
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ class Severity(IntEnum):
|
|||
return Severity.from_string(alert.labels.get("severity", "OK"))
|
||||
|
||||
@property
|
||||
def css(self):
|
||||
def css(self) -> str:
|
||||
classes = {self.OK: "success", self.WARNING: "warning", self.ERROR: "danger"}
|
||||
return classes[self]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.css
|
||||
|
||||
|
||||
|
|
|
@ -16,21 +16,21 @@ class TimestampFile(object):
|
|||
with self.path.open("w") as f:
|
||||
f.write(time.strftime(_blessed_date_format).encode("utf-8"))
|
||||
|
||||
def now(self):
|
||||
def now(self) -> None:
|
||||
self.set(current_time())
|
||||
|
||||
def getStr(self):
|
||||
def getStr(self) -> str:
|
||||
if not self.path.exists():
|
||||
return ""
|
||||
with self.path.open("r") as f:
|
||||
return f.read().decode("utf-8")
|
||||
|
||||
|
||||
def current_time():
|
||||
def current_time() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
def current_timestamp():
|
||||
def current_timestamp() -> str:
|
||||
return current_time().strftime(_blessed_date_format)
|
||||
|
||||
|
||||
|
|
61
typings/klein/__init__.pyi
Normal file
61
typings/klein/__init__.pyi
Normal file
|
@ -0,0 +1,61 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ._app import (
|
||||
Klein,
|
||||
KleinErrorHandler,
|
||||
KleinRenderable,
|
||||
KleinRouteHandler,
|
||||
handle_errors,
|
||||
route,
|
||||
run,
|
||||
subroute,
|
||||
url_for,
|
||||
urlFor,
|
||||
)
|
||||
from ._dihttp import RequestComponent, RequestURL, Response
|
||||
from ._form import Field, FieldValues, Form, RenderableForm
|
||||
from ._plating import Plating
|
||||
from ._requirer import Requirer
|
||||
from ._session import Authorization, SessionProcurer
|
||||
from ._version import __version__ as _incremental_version
|
||||
from .resource import _SpecialModuleObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
resource = ...
|
||||
else: ...
|
||||
__all__ = (
|
||||
"Klein",
|
||||
"KleinErrorHandler",
|
||||
"KleinRenderable",
|
||||
"KleinRouteHandler",
|
||||
"Plating",
|
||||
"Field",
|
||||
"FieldValues",
|
||||
"Form",
|
||||
"RequestComponent",
|
||||
"RequestURL",
|
||||
"Response",
|
||||
"RenderableForm",
|
||||
"SessionProcurer",
|
||||
"Authorization",
|
||||
"Requirer",
|
||||
"__author__",
|
||||
"__copyright__",
|
||||
"__license__",
|
||||
"__version__",
|
||||
"handle_errors",
|
||||
"resource",
|
||||
"route",
|
||||
"run",
|
||||
"subroute",
|
||||
"urlFor",
|
||||
"url_for",
|
||||
)
|
||||
__version__ = ...
|
||||
__author__ = ...
|
||||
__license__ = ...
|
||||
__copyright__ = ...
|
315
typings/klein/_app.pyi
Normal file
315
typings/klein/_app.pyi
Normal file
|
@ -0,0 +1,315 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from contextlib import contextmanager
|
||||
from typing import (
|
||||
IO,
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.web.iweb import IRenderable, IRequest
|
||||
from twisted.web.resource import IResource
|
||||
from twisted.web.server import Request
|
||||
from werkzeug.routing import Map, MapAdapter
|
||||
|
||||
from ._interfaces import IKleinRequest, KleinQueryValue
|
||||
from ._resource import KleinResource
|
||||
|
||||
"""
|
||||
Applications are great. Lets have more of them.
|
||||
"""
|
||||
KleinSynchronousRenderable = Union[str, bytes, IResource, IRenderable]
|
||||
KleinRenderable = Union[
|
||||
KleinSynchronousRenderable, Awaitable[KleinSynchronousRenderable]
|
||||
]
|
||||
|
||||
class KleinRouteFunction(Protocol):
|
||||
def __call__(_self, request: IRequest) -> KleinRenderable:
|
||||
"""
|
||||
Function that, when decorated by L{Klein.route}, handles a Klein
|
||||
request.
|
||||
"""
|
||||
...
|
||||
|
||||
class KleinRouteMethod(Protocol):
|
||||
def __call__(_self, self: Any, request: IRequest) -> KleinRenderable:
|
||||
"""
|
||||
Method that, when decorated by L{Klein.route}, handles a Klein
|
||||
request.
|
||||
"""
|
||||
...
|
||||
|
||||
class KleinErrorFunction(Protocol):
|
||||
def __call__(_self, request: IRequest, failure: Failure) -> KleinRenderable:
|
||||
"""
|
||||
Function that, when registered with L{Klein.handle_errors}, handles
|
||||
errors raised during request routing.
|
||||
"""
|
||||
...
|
||||
|
||||
class KleinErrorMethod(Protocol):
|
||||
def __call__(
|
||||
_self, self: Optional[Klein], request: IRequest, failure: Failure
|
||||
) -> KleinRenderable:
|
||||
"""
|
||||
Method that, when registered with L{Klein.handle_errors}, handles
|
||||
errors raised during request routing.
|
||||
"""
|
||||
...
|
||||
|
||||
KleinRouteHandler = Union[KleinRouteFunction, KleinRouteMethod]
|
||||
KleinErrorHandler = Union[KleinErrorFunction, KleinErrorMethod]
|
||||
|
||||
def buildURL(
|
||||
mapper: MapAdapter,
|
||||
endpoint: str,
|
||||
values: Optional[Mapping[str, KleinQueryValue]] = ...,
|
||||
method: Optional[str] = ...,
|
||||
force_external: bool = ...,
|
||||
append_unknown: bool = ...,
|
||||
) -> str: ...
|
||||
@implementer(IKleinRequest)
|
||||
class KleinRequest:
|
||||
def __init__(self, request: Request) -> None: ...
|
||||
def url_for(
|
||||
self,
|
||||
endpoint: str,
|
||||
values: Optional[Mapping[str, KleinQueryValue]] = ...,
|
||||
method: Optional[str] = ...,
|
||||
force_external: bool = ...,
|
||||
append_unknown: bool = ...,
|
||||
) -> str: ...
|
||||
|
||||
ErrorHandlers = List[Tuple[List[Type[Exception]], KleinErrorHandler]]
|
||||
|
||||
class Klein:
|
||||
"""
|
||||
L{Klein} is an object which is responsible for maintaining the routing
|
||||
configuration of our application.
|
||||
|
||||
@ivar _url_map: A C{werkzeug.routing.Map} object which will be used for
|
||||
routing resolution.
|
||||
@ivar _endpoints: A C{dict} mapping endpoint names to handler functions.
|
||||
"""
|
||||
|
||||
_subroute_segments = ...
|
||||
def __init__(self) -> None: ...
|
||||
def __eq__(self, other: Any) -> bool: ...
|
||||
def __ne__(self, other: Any) -> bool: ...
|
||||
@property
|
||||
def url_map(self) -> Map:
|
||||
"""
|
||||
Read only property exposing L{Klein._url_map}.
|
||||
"""
|
||||
...
|
||||
@property
|
||||
def endpoints(self) -> Dict[str, KleinRouteHandler]:
|
||||
"""
|
||||
Read only property exposing L{Klein._endpoints}.
|
||||
"""
|
||||
...
|
||||
def execute_endpoint(
|
||||
self, endpoint: str, request: IRequest, *args: Any, **kwargs: Any
|
||||
) -> KleinRenderable:
|
||||
"""
|
||||
Execute the named endpoint with all arguments and possibly a bound
|
||||
instance.
|
||||
"""
|
||||
...
|
||||
def execute_error_handler(
|
||||
self, handler: KleinErrorMethod, request: IRequest, failure: Failure
|
||||
) -> KleinRenderable:
|
||||
"""
|
||||
Execute the passed error handler, possibly with a bound instance.
|
||||
"""
|
||||
...
|
||||
def resource(self) -> KleinResource:
|
||||
"""
|
||||
Return an L{IResource} which suitably wraps this app.
|
||||
|
||||
@returns: An L{IResource}
|
||||
"""
|
||||
...
|
||||
def __get__(self, instance: Any, owner: object) -> Klein:
|
||||
"""
|
||||
Get an instance of L{Klein} bound to C{instance}.
|
||||
"""
|
||||
...
|
||||
def route(
|
||||
self, url: str, *args: Any, **kwargs: Any
|
||||
) -> Callable[[KleinRouteHandler], KleinRouteHandler]:
|
||||
"""
|
||||
Add a new handler for C{url} passing C{args} and C{kwargs} directly to
|
||||
C{werkzeug.routing.Rule}. The handler function will be passed at least
|
||||
one argument an L{twisted.web.server.Request} and any keyword arguments
|
||||
taken from the C{url} pattern.
|
||||
|
||||
::
|
||||
@app.route("/")
|
||||
def index(request):
|
||||
return "Hello"
|
||||
|
||||
@param url: A werkzeug URL pattern given to C{werkzeug.routing.Rule}.
|
||||
@param branch: A bool indiciated if a branch endpoint should
|
||||
be added that allows all child path segments that don't
|
||||
match some other route to be consumed. Default C{False}.
|
||||
|
||||
@returns: decorated handler function.
|
||||
"""
|
||||
...
|
||||
@contextmanager
|
||||
def subroute(self, prefix: str) -> Iterator[Klein]:
|
||||
"""
|
||||
Within this block, C{@route} adds rules to a
|
||||
C{werkzeug.routing.Submount}.
|
||||
|
||||
This is implemented by tinkering with the instance's C{_url_map}
|
||||
variable. A context manager allows us to gracefully use the pattern of
|
||||
"change a variable, do some things with the new value, then put it back
|
||||
to how it was before.
|
||||
|
||||
Named "subroute" to try and give callers a better idea of its
|
||||
relationship to C{@route}.
|
||||
|
||||
Usage:
|
||||
::
|
||||
with app.subroute("/prefix") as app:
|
||||
@app.route("/foo")
|
||||
def foo_handler(request):
|
||||
return 'I respond to /prefix/foo'
|
||||
|
||||
@param prefix: The string that will be prepended to the paths of all
|
||||
routes established during the with-block.
|
||||
"""
|
||||
|
||||
class SubmountMap: ...
|
||||
|
||||
@overload
|
||||
def handle_errors(
|
||||
self, f_or_exception: KleinErrorHandler, *additional_exceptions: Type[Exception]
|
||||
) -> Callable[[KleinErrorHandler], Callable]: ...
|
||||
@overload
|
||||
def handle_errors(
|
||||
self, f_or_exception: Type[Exception], *additional_exceptions: Type[Exception]
|
||||
) -> Callable[[KleinErrorHandler], Callable]: ...
|
||||
def handle_errors(
|
||||
self,
|
||||
f_or_exception: Union[KleinErrorHandler, Type[Exception]],
|
||||
*additional_exceptions: Type[Exception],
|
||||
) -> Callable[[KleinErrorHandler], Callable]:
|
||||
"""
|
||||
Register an error handler. This decorator supports two syntaxes. The
|
||||
simpler of these can be used to register a handler for all C{Exception}
|
||||
types::
|
||||
|
||||
@app.handle_errors
|
||||
def error_handler(request, failure):
|
||||
request.setResponseCode(500)
|
||||
return 'Uh oh'
|
||||
|
||||
Alternately, a handler can be registered for one or more specific
|
||||
C{Exception} types::
|
||||
|
||||
@app.handle_errors(EncodingError, ValidationError):
|
||||
def error_handler(request, failure)
|
||||
request.setResponseCode(400)
|
||||
return failure.getTraceback()
|
||||
|
||||
The handler will be passed a L{twisted.web.server.Request} as well as a
|
||||
L{twisted.python.failure.Failure} instance. Error handlers may return a
|
||||
deferred, a failure or a response body.
|
||||
|
||||
If more than one error handler is registered, the handlers will be
|
||||
executed in the order in which they are defined, until a handler is
|
||||
encountered which completes successfully. If no handler completes
|
||||
successfully, L{twisted.web.server.Request}'s processingFailed() method
|
||||
will be called.
|
||||
|
||||
In addition to handling errors that occur within a L{KleinRouteHandler},
|
||||
error handlers also handle any L{werkzeug.exceptions.HTTPException}
|
||||
which is raised during request routing.
|
||||
|
||||
In particular, C{werkzeug.exceptions.NotFound} will be raised if no
|
||||
matching route is found, so to return a custom 404 users can do the
|
||||
following::
|
||||
|
||||
@app.handle_errors(NotFound)
|
||||
def error_handler(request, failure):
|
||||
request.setResponseCode(404)
|
||||
return 'Not found'
|
||||
|
||||
@param f_or_exception: An error handler function, or an C{Exception}
|
||||
subclass to scope the decorated handler to.
|
||||
@param additional_exceptions: Additional C{Exception} subclasses to
|
||||
scope the decorated function to.
|
||||
|
||||
@returns: decorated error handler function.
|
||||
"""
|
||||
...
|
||||
def urlFor(
|
||||
self,
|
||||
request: IRequest,
|
||||
endpoint: str,
|
||||
values: Optional[Mapping[str, KleinQueryValue]] = ...,
|
||||
method: Optional[str] = ...,
|
||||
force_external: bool = ...,
|
||||
append_unknown: bool = ...,
|
||||
) -> str: ...
|
||||
|
||||
url_for = ...
|
||||
def run(
|
||||
self,
|
||||
host: Optional[str] = ...,
|
||||
port: Optional[int] = ...,
|
||||
logFile: Optional[IO] = ...,
|
||||
endpoint_description: Optional[str] = ...,
|
||||
displayTracebacks: bool = ...,
|
||||
) -> None:
|
||||
"""
|
||||
Run a minimal twisted.web server on the specified C{port}, bound to the
|
||||
interface specified by C{host} and logging to C{logFile}.
|
||||
|
||||
This function will run the default reactor for your platform and so
|
||||
will block the main thread of your application. It should be the last
|
||||
thing your klein application does.
|
||||
|
||||
@param host: The hostname or IP address to bind the listening socket
|
||||
to. "0.0.0.0" will allow you to listen on all interfaces, and
|
||||
"127.0.0.1" will allow you to listen on just the loopback
|
||||
interface.
|
||||
|
||||
@param port: The TCP port to accept HTTP requests on.
|
||||
|
||||
@param logFile: The file object to log to, by default C{sys.stdout}
|
||||
|
||||
@param endpoint_description: specification of endpoint. Must contain
|
||||
protocol, port and interface. May contain other optional arguments,
|
||||
e.g. to use SSL: "ssl:443:privateKey=key.pem:certKey=crt.pem"
|
||||
|
||||
@param displayTracebacks: Weather a processing error will result in
|
||||
a page displaying the traceback with debugging information or not.
|
||||
"""
|
||||
...
|
||||
|
||||
_globalKleinApp = ...
|
||||
route = ...
|
||||
run = ...
|
||||
subroute = ...
|
||||
resource = ...
|
||||
handle_errors = ...
|
||||
urlFor = ...
|
64
typings/klein/_decorators.pyi
Normal file
64
typings/klein/_decorators.pyi
Normal file
|
@ -0,0 +1,64 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Callable, Optional, TypeVar
|
||||
|
||||
C = TypeVar("C", bound=Callable)
|
||||
|
||||
def bindable(bindable: C) -> C:
|
||||
"""
|
||||
Mark a method as a "bindable" method.
|
||||
|
||||
If a L{Klein.app} resource is found on an instance object (i.e. is returned
|
||||
from C{YourObject().app.resource()}), it will pass C{self} from that
|
||||
instance to all of its routes, making a signature of 2 arguments: C{self}
|
||||
and C{request} However, if it's found globally (i.e. C{app = Klein()};
|
||||
C{@app.route(...)} at global scope), then it will only receive one:
|
||||
C{request}. However, for decorators that must be able to live between
|
||||
C{@route} and the user's function, but still need to manipulate the
|
||||
C{request} object, they need to be invoked with a consistent argument
|
||||
signature. A method decorated with C{@bindable} will therefore always take
|
||||
C{instance, request} as its first two arguments, even if C{instance} is
|
||||
C{None} when the L{Klein} object is not bound to an instance.
|
||||
|
||||
@return: its argument, modified to mark it as unconditionally requiring an
|
||||
instance argument.
|
||||
"""
|
||||
...
|
||||
|
||||
def modified(
|
||||
modification: str, original: Callable, modifier: Optional[Callable] = ...
|
||||
) -> Callable:
|
||||
"""
|
||||
Annotate a callable as a modified wrapper of an original callable.
|
||||
|
||||
@param modification: A name for the type of modification, for example "form
|
||||
processor" or "request forwarder"; this will be tacked on to the name
|
||||
of the resulting function.
|
||||
|
||||
@param modifier: Another decorator which, if given, will be applied to the
|
||||
function that decorates this function. Additionally, I{any new
|
||||
attributes} set on the decorated function by C{modifier} will be
|
||||
I{copied to} C{original}. This allows attributes set by "inner"
|
||||
decorators such as L{klein.Form.handler} and L{klein.app.Klein.route}
|
||||
to set attributes that will be visible at the top level.
|
||||
|
||||
@return: A new callable; this may have a different argument signature or
|
||||
return value, and is only related to C{original} in the sense that it
|
||||
likely calls it.
|
||||
"""
|
||||
...
|
||||
|
||||
def named(name: str) -> Callable[[C], C]:
|
||||
"""
|
||||
Change the name of a function to the given name.
|
||||
"""
|
||||
...
|
||||
|
||||
def originalName(function: Callable) -> str:
|
||||
"""
|
||||
Get the original, user-specified name of C{function}, chasing back any
|
||||
wrappers applied with C{modified}.
|
||||
"""
|
||||
...
|
90
typings/klein/_dihttp.pyi
Normal file
90
typings/klein/_dihttp.pyi
Normal file
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict, Mapping, Sequence, Type, Union
|
||||
|
||||
from zope.interface import Interface, implementer, provider
|
||||
|
||||
import attr
|
||||
from hyperlink import DecodedURL
|
||||
from twisted.python.components import Componentized
|
||||
from twisted.web.iweb import IRequest
|
||||
|
||||
from .interfaces import IDependencyInjector, IRequestLifecycle, IRequiredParameter
|
||||
|
||||
"""
|
||||
Dependency-Injected HTTP metadata.
|
||||
"""
|
||||
|
||||
def urlFromRequest(request: IRequest) -> DecodedURL: ...
|
||||
@provider(IRequiredParameter, IDependencyInjector)
|
||||
class RequestURL:
|
||||
"""
|
||||
Require a hyperlink L{DecodedURL} object from a L{Requirer}.
|
||||
|
||||
@since: Klein NEXT
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def registerInjector(
|
||||
cls,
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
requestLifecycle: IRequestLifecycle,
|
||||
) -> IDependencyInjector: ...
|
||||
@classmethod
|
||||
def injectValue(
|
||||
cls, instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> DecodedURL: ...
|
||||
@classmethod
|
||||
def finalize(cls) -> None:
|
||||
"Nothing to do upon finalization."
|
||||
...
|
||||
|
||||
@implementer(IRequiredParameter, IDependencyInjector)
|
||||
@attr.frozen
|
||||
class RequestComponent:
|
||||
"""
|
||||
Require a hyperlink L{DecodedURL} object from a L{Requirer}.
|
||||
|
||||
@since: Klein NEXT
|
||||
"""
|
||||
|
||||
interface: Type[Interface]
|
||||
def registerInjector(
|
||||
self,
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
requestLifecycle: IRequestLifecycle,
|
||||
) -> IDependencyInjector: ...
|
||||
def injectValue(
|
||||
self, instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> DecodedURL: ...
|
||||
def finalize(cls) -> None:
|
||||
"Nothing to do upon finalization."
|
||||
...
|
||||
|
||||
@attr.s(auto_attribs=True, frozen=True)
|
||||
class Response:
|
||||
"""
|
||||
Metadata about an HTTP response, with an object that Klein knows how to
|
||||
understand.
|
||||
|
||||
This includes:
|
||||
|
||||
- an HTTP response code
|
||||
|
||||
- some HTTP headers
|
||||
|
||||
- a body object, which can be anything else Klein understands; for
|
||||
example, an IResource, an IRenderable, str, bytes, etc.
|
||||
|
||||
@since: Klein NEXT
|
||||
"""
|
||||
|
||||
code: int = ...
|
||||
headers: Mapping[
|
||||
Union[str, bytes], Union[str, bytes, Sequence[Union[str, bytes]]]
|
||||
] = ...
|
||||
body: Any = ...
|
476
typings/klein/_form.pyi
Normal file
476
typings/klein/_form.pyi
Normal file
|
@ -0,0 +1,476 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
AnyStr,
|
||||
Callable,
|
||||
Dict,
|
||||
Generator,
|
||||
Iterable,
|
||||
List,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
)
|
||||
|
||||
from zope.interface import Interface, implementer
|
||||
|
||||
import attr
|
||||
from twisted.internet.defer import Deferred, inlineCallbacks
|
||||
from twisted.python.components import Componentized
|
||||
from twisted.web.iweb import IRenderable, IRequest
|
||||
from twisted.web.resource import Resource
|
||||
from twisted.web.template import Element, Tag
|
||||
|
||||
from ._app import KleinRenderable
|
||||
from ._decorators import bindable
|
||||
from .interfaces import (
|
||||
IDependencyInjector,
|
||||
IRequestLifecycle,
|
||||
IRequiredParameter,
|
||||
ISession,
|
||||
ValidationError,
|
||||
)
|
||||
|
||||
class CrossSiteRequestForgery(Resource):
|
||||
"""
|
||||
Cross site request forgery detected. Request aborted.
|
||||
"""
|
||||
|
||||
def __init__(self, message: str) -> None: ...
|
||||
def render(self, request: IRequest) -> bytes:
|
||||
"""
|
||||
For all HTTP methods, return a 403.
|
||||
"""
|
||||
...
|
||||
|
||||
CSRF_PROTECTION = ...
|
||||
|
||||
def textConverter(value: AnyStr) -> str:
|
||||
"""
|
||||
Converter for form values (which may be any type of string) into text.
|
||||
"""
|
||||
...
|
||||
|
||||
class IParsedJSONBody(Interface):
|
||||
"""
|
||||
Marker interface for the dict parsed from the request body's JSON contents.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
@implementer(IRequiredParameter)
|
||||
@attr.s(auto_attribs=True, frozen=True)
|
||||
class Field:
|
||||
"""
|
||||
A L{Field} is a static part of a L{Form}.
|
||||
|
||||
@ivar converter: The converter.
|
||||
"""
|
||||
|
||||
converter: Callable[[AnyStr], Any]
|
||||
formInputType: str
|
||||
pythonArgumentName: Optional[str] = ...
|
||||
formFieldName: Optional[str] = ...
|
||||
formLabel: Optional[str] = ...
|
||||
default: Optional[Any] = ...
|
||||
required: bool = ...
|
||||
noLabel: bool = ...
|
||||
value: str = ...
|
||||
error: Optional[ValidationError] = ...
|
||||
def registerInjector(
|
||||
self,
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
requestLifecycle: IRequestLifecycle,
|
||||
) -> IDependencyInjector:
|
||||
"""
|
||||
Register this form field as a dependency injector.
|
||||
"""
|
||||
...
|
||||
def maybeNamed(self, name: str) -> Field:
|
||||
"""
|
||||
Create a new L{Field} like this one, but with all the name default
|
||||
values filled in.
|
||||
|
||||
@param name: the name.
|
||||
"""
|
||||
...
|
||||
def asTags(self) -> Iterable[Tag]:
|
||||
"""
|
||||
Convert this L{Field} into some stuff that can be rendered in a
|
||||
L{twisted.web.template}.
|
||||
|
||||
@return: A new set of tags to include in a template.
|
||||
"""
|
||||
...
|
||||
def extractValue(self, request: IRequest) -> Any:
|
||||
"""
|
||||
Extract a value from the request.
|
||||
|
||||
In the case of key/value form posts, this attempts to reliably make the
|
||||
value into str. In the case of a JSON post, however, it will simply
|
||||
extract the value from the top-level dictionary, which means it could
|
||||
be any arrangement of JSON-serializiable objects.
|
||||
"""
|
||||
...
|
||||
def validateValue(self, value: Any) -> Any:
|
||||
"""
|
||||
Validate the given text and return a converted Python object to use, or
|
||||
fail with L{ValidationError}.
|
||||
|
||||
@param value: The value that was extracted by L{Field.extractValue}.
|
||||
|
||||
@return: The converted value.
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def text(cls, **kw: Any) -> Field:
|
||||
"""
|
||||
Shorthand for a form field that contains a short string, and will be
|
||||
rendered as a plain <input>.
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def password(cls, **kw: Any) -> Field:
|
||||
"""
|
||||
Shorthand for a form field that, like L{text}, contains a short string,
|
||||
but should be obscured when typed (and, to the extent possible,
|
||||
obscured in other sensitive contexts, such as logging.)
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def hidden(cls, name: str, value: str, **kw: Any) -> Field:
|
||||
"""
|
||||
Shorthand for a hidden field.
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def number(
|
||||
cls,
|
||||
minimum: Optional[int] = ...,
|
||||
maximum: Optional[int] = ...,
|
||||
kind: Type = ...,
|
||||
**kw: Any,
|
||||
) -> Field:
|
||||
"""
|
||||
An integer within the range [minimum, maximum].
|
||||
"""
|
||||
...
|
||||
@classmethod
|
||||
def submit(cls, value: str) -> Field:
|
||||
"""
|
||||
A field representing a submit button, with a value (displayed on the
|
||||
button).
|
||||
"""
|
||||
...
|
||||
|
||||
@implementer(IRenderable)
|
||||
@attr.s(auto_attribs=True)
|
||||
class RenderableForm:
|
||||
"""
|
||||
An L{IRenderable} representing a renderable form.
|
||||
|
||||
@ivar prevalidationValues: a L{dict} mapping {L{Field}: L{list} of
|
||||
L{str}}, representing the value that each field received as part of
|
||||
the request.
|
||||
|
||||
@ivar validationErrors: a L{dict} mapping {L{Field}: L{ValidationError}}
|
||||
"""
|
||||
|
||||
_form: IForm
|
||||
_session: ISession
|
||||
_action: str
|
||||
_method: str
|
||||
_enctype: str
|
||||
_encoding: str
|
||||
prevalidationValues: Dict[Field, Optional[str]] = ...
|
||||
validationErrors: Dict[Field, ValidationError] = ...
|
||||
ENCTYPE_FORM_DATA = ...
|
||||
ENCTYPE_URL_ENCODED = ...
|
||||
def lookupRenderMethod(self, name: str) -> NoReturn:
|
||||
"""
|
||||
Form renderers don't supply any render methods, so this just always
|
||||
raises L{MissingRenderMethod}.
|
||||
"""
|
||||
...
|
||||
def render(self, request: IRequest) -> Tag:
|
||||
"""
|
||||
Render this form to the given request.
|
||||
"""
|
||||
...
|
||||
def glue(self) -> List[Tag]:
|
||||
"""
|
||||
Provide any glue necessary to render this form; this must be dropped
|
||||
into the template within the C{<form>} tag.
|
||||
|
||||
Presently, this glue includes only the CSRF token argument, but Klein
|
||||
reserves the right to add arbitrary HTML here. This should not create
|
||||
any user-visible content, however.
|
||||
|
||||
@return: some HTML elements in the form of renderable objects for
|
||||
L{twisted.web.template}
|
||||
"""
|
||||
...
|
||||
|
||||
@bindable
|
||||
def defaultValidationFailureHandler(
|
||||
instance: Optional[object], request: IRequest, fieldValues: FieldValues
|
||||
) -> Element:
|
||||
"""
|
||||
This is the default validation failure handler, which will be used by form
|
||||
handlers (i.e. any routes which use L{klein.Requirer} to require a field)
|
||||
in the case of any input validation failure when no other validation
|
||||
failure handler is registered via L{Form.onValidationFailureFor}.
|
||||
|
||||
Its behavior is to simply return an HTML rendering of the form object,
|
||||
which includes inline information about fields which failed to validate.
|
||||
|
||||
@param instance: The instance associated with the router that the form
|
||||
handler was handled on.
|
||||
@param request: The request including the form submission.
|
||||
@return: Any object acceptable from a Klein route.
|
||||
"""
|
||||
...
|
||||
|
||||
_requirerFunctionWithForm = Any
|
||||
_routeCallable = Any
|
||||
|
||||
class IProtoForm(Interface):
|
||||
"""
|
||||
Marker interface for L{ProtoForm}.
|
||||
"""
|
||||
|
||||
fields: Sequence[Field] = ...
|
||||
def addField(field: Field) -> FieldInjector:
|
||||
"""
|
||||
Add the given field to the form ultimately created here.
|
||||
"""
|
||||
...
|
||||
|
||||
class IForm(Interface):
|
||||
"""
|
||||
Marker interface for form attached to dependency injection components.
|
||||
"""
|
||||
|
||||
fields: Sequence[Field] = ...
|
||||
def populateRequestValues(
|
||||
injectionComponents: Componentized, instance: Any, request: IRequest
|
||||
) -> Deferred:
|
||||
"""
|
||||
Extract the values present in this request and populate a
|
||||
L{FieldValues} object.
|
||||
"""
|
||||
...
|
||||
|
||||
@implementer(IProtoForm)
|
||||
@attr.s(auto_attribs=True)
|
||||
class ProtoForm:
|
||||
"""
|
||||
Form-builder.
|
||||
"""
|
||||
|
||||
_componentized: Componentized
|
||||
_lifecycle: IRequestLifecycle
|
||||
fields: List[Field] = ...
|
||||
@classmethod
|
||||
def fromComponentized(cls, componentized: Componentized) -> ProtoForm:
|
||||
"""
|
||||
Create a ProtoForm from a componentized object.
|
||||
"""
|
||||
...
|
||||
def addField(self, field: Field) -> FieldInjector: ...
|
||||
|
||||
class IFieldValues(Interface):
|
||||
"""
|
||||
Marker interface for parsed fields.
|
||||
"""
|
||||
|
||||
form: IForm = ...
|
||||
arguments: Dict[str, Any] = ...
|
||||
prevalidationValues: Dict[Field, Optional[str]] = ...
|
||||
validationErrors: Dict[Field, ValidationError] = ...
|
||||
def validate(instance: Any, request: IRequest) -> Deferred:
|
||||
"""
|
||||
If any validation errors have occurred, raise a relevant exception.
|
||||
"""
|
||||
...
|
||||
|
||||
@implementer(IFieldValues)
|
||||
@attr.s(auto_attribs=True)
|
||||
class FieldValues:
|
||||
"""
|
||||
Reified post-parsing values for HTTP form submission.
|
||||
"""
|
||||
|
||||
form: Form
|
||||
arguments: Dict[str, Any]
|
||||
prevalidationValues: Dict[Field, Optional[str]]
|
||||
validationErrors: Dict[Field, ValidationError]
|
||||
_injectionComponents: Componentized
|
||||
@inlineCallbacks
|
||||
def validate(
|
||||
self, instance: Any, request: IRequest
|
||||
) -> Generator[Any, object, None]: ...
|
||||
|
||||
@implementer(IDependencyInjector)
|
||||
@attr.s(auto_attribs=True)
|
||||
class FieldInjector:
|
||||
"""
|
||||
Field injector.
|
||||
"""
|
||||
|
||||
_componentized: Componentized
|
||||
_field: Field
|
||||
_lifecycle: IRequestLifecycle
|
||||
def injectValue(
|
||||
self, instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> Any:
|
||||
"""
|
||||
Inject the given value into the form.
|
||||
"""
|
||||
...
|
||||
def finalize(self) -> None:
|
||||
"""
|
||||
Finalize this ProtoForm into a real form.
|
||||
"""
|
||||
...
|
||||
|
||||
class IValidationFailureHandler(Interface):
|
||||
"""
|
||||
Validation failure handler callable interface.
|
||||
"""
|
||||
|
||||
def __call__(request: IRequest) -> KleinRenderable:
|
||||
"""
|
||||
Called to handle a validation failure.
|
||||
"""
|
||||
...
|
||||
|
||||
def checkCSRF(request: IRequest) -> None:
|
||||
"""
|
||||
Check the request for cross-site request forgery, raising an EarlyExit if
|
||||
it is found.
|
||||
"""
|
||||
...
|
||||
|
||||
@implementer(IForm)
|
||||
@attr.s(auto_attribs=True, hash=False)
|
||||
class Form:
|
||||
"""
|
||||
A L{Form} is a collection of fields attached to a function.
|
||||
"""
|
||||
|
||||
fields: Sequence[Field]
|
||||
@staticmethod
|
||||
def onValidationFailureFor(
|
||||
handler: _requirerFunctionWithForm,
|
||||
) -> Callable[[Callable], Callable]:
|
||||
"""
|
||||
Register a function to be run in the event of a validation failure for
|
||||
the input to a particular form handler.
|
||||
|
||||
Generally used like so::
|
||||
|
||||
requirer = Requirer(...)
|
||||
@requirer.prerequisite([ISession])
|
||||
def procureSession(request):
|
||||
return SessionProcurer(...).procureSession(request)
|
||||
router = Klein()
|
||||
@requirer.require(router.route("/", methods=['POST']),
|
||||
someField=Field.text())
|
||||
def formHandler(someField):
|
||||
...
|
||||
# Handle input validation failures for handleForm
|
||||
@Form.onValidationFailureFor(formHandler)
|
||||
def handleValidationFailures(request, fieldValues):
|
||||
return "Your inputs didn't validate."
|
||||
|
||||
@see: L{defaultValidationFailureHandler} for a more detailed
|
||||
description of the decorated function's expected signature. The
|
||||
additional parameter it is passed is a L{FieldValues} instance.
|
||||
|
||||
@param handler: The form handler - i.e. function decorated by
|
||||
L{Form.handler} - for which the decorated function will handle
|
||||
validation failures.
|
||||
|
||||
@return: a decorator that decorates a function with the signature
|
||||
C{(request, form) -> thing klein can render}.
|
||||
"""
|
||||
...
|
||||
@inlineCallbacks
|
||||
def populateRequestValues(
|
||||
self, injectionComponents: Componentized, instance: Any, request: IRequest
|
||||
) -> Generator[Any, object, None]: ...
|
||||
@classmethod
|
||||
def rendererFor(
|
||||
cls,
|
||||
decoratedFunction: _requirerFunctionWithForm,
|
||||
action: str,
|
||||
method: str = ...,
|
||||
enctype: str = ...,
|
||||
encoding: str = ...,
|
||||
) -> RenderableFormParam:
|
||||
"""
|
||||
A form parameter that can render a form declared as a number of fields
|
||||
on another route.
|
||||
|
||||
Use like so::
|
||||
|
||||
class MyFormApp:
|
||||
router = Klein()
|
||||
requirer = Requirer()
|
||||
|
||||
@requirer.require(
|
||||
router.route("/handle-form", methods=["POST"]),
|
||||
name=Field.text(), value=Field.integer(),
|
||||
)
|
||||
def formRoute(self, name, value):
|
||||
...
|
||||
|
||||
@requirer.require(
|
||||
router.route("/show-form", methods=["GET"]),
|
||||
form=Form.rendererFor(formRoute)
|
||||
)
|
||||
def showForm(self, form):
|
||||
return form
|
||||
|
||||
As a L{RenderableForm} provides L{IRenderable}, you may return the
|
||||
parameter directly
|
||||
"""
|
||||
...
|
||||
|
||||
@implementer(IRequiredParameter, IDependencyInjector)
|
||||
@attr.s(auto_attribs=True)
|
||||
class RenderableFormParam:
|
||||
"""
|
||||
A L{RenderableFormParam} implements L{IRequiredParameter} and
|
||||
L{IDependencyInjector} to provide a L{RenderableForm} to your route.
|
||||
"""
|
||||
|
||||
_form: IForm
|
||||
_action: str
|
||||
_method: str
|
||||
_enctype: str
|
||||
_encoding: str
|
||||
def registerInjector(
|
||||
self,
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
requestLifecycle: IRequestLifecycle,
|
||||
) -> RenderableFormParam: ...
|
||||
def injectValue(
|
||||
self, instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> RenderableForm:
|
||||
"""
|
||||
Create the renderable form from the request.
|
||||
"""
|
||||
...
|
||||
def finalize(self) -> None:
|
||||
"""
|
||||
Nothing to do upon finalization.
|
||||
"""
|
||||
...
|
94
typings/klein/_headers.pyi
Normal file
94
typings/klein/_headers.pyi
Normal file
|
@ -0,0 +1,94 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import AnyStr, Iterable, Tuple, Union
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
|
||||
from ._imessage import (
|
||||
IHTTPHeaders,
|
||||
IMutableHTTPHeaders,
|
||||
MutableRawHeaders,
|
||||
RawHeader,
|
||||
RawHeaders,
|
||||
)
|
||||
|
||||
"""
|
||||
HTTP headers API.
|
||||
"""
|
||||
__all__ = ()
|
||||
String = Union[bytes, str]
|
||||
HEADER_NAME_ENCODING = ...
|
||||
HEADER_VALUE_ENCODING = ...
|
||||
|
||||
def headerNameAsBytes(name: String) -> bytes:
|
||||
"""
|
||||
Convert a header name to bytes if necessary.
|
||||
"""
|
||||
...
|
||||
|
||||
def headerNameAsText(name: String) -> str:
|
||||
"""
|
||||
Convert a header name to str if necessary.
|
||||
"""
|
||||
...
|
||||
|
||||
def headerValueAsBytes(value: String) -> bytes:
|
||||
"""
|
||||
Convert a header value to bytes if necessary.
|
||||
"""
|
||||
...
|
||||
|
||||
def headerValueAsText(value: String) -> str:
|
||||
"""
|
||||
Convert a header value to str if necessary.
|
||||
"""
|
||||
...
|
||||
|
||||
def normalizeHeaderName(name: AnyStr) -> AnyStr:
|
||||
"""
|
||||
Normalize a header name.
|
||||
"""
|
||||
...
|
||||
|
||||
def normalizeRawHeaders(
|
||||
headerPairs: Iterable[Iterable[String]],
|
||||
) -> Iterable[RawHeader]: ...
|
||||
def normalizeRawHeadersFrozen(headerPairs: Iterable[Iterable[bytes]]) -> RawHeaders: ...
|
||||
def normalizeRawHeadersMutable(
|
||||
headerPairs: Iterable[Iterable[bytes]],
|
||||
) -> MutableRawHeaders: ...
|
||||
def getFromRawHeaders(rawHeaders: RawHeaders, name: AnyStr) -> Iterable[AnyStr]:
|
||||
"""
|
||||
Get a value from raw headers.
|
||||
"""
|
||||
...
|
||||
|
||||
def rawHeaderName(name: String) -> bytes: ...
|
||||
def rawHeaderNameAndValue(name: String, value: String) -> Tuple[bytes, bytes]: ...
|
||||
@implementer(IHTTPHeaders)
|
||||
@attrs(frozen=True)
|
||||
class FrozenHTTPHeaders:
|
||||
"""
|
||||
Immutable HTTP entity headers.
|
||||
"""
|
||||
|
||||
rawHeaders: RawHeaders = ...
|
||||
def getValues(self, name: AnyStr) -> Iterable[AnyStr]: ...
|
||||
|
||||
@implementer(IMutableHTTPHeaders)
|
||||
@attrs(frozen=True)
|
||||
class MutableHTTPHeaders:
|
||||
"""
|
||||
Mutable HTTP entity headers.
|
||||
"""
|
||||
|
||||
_rawHeaders: MutableRawHeaders = ...
|
||||
@property
|
||||
def rawHeaders(self) -> RawHeaders: ...
|
||||
def getValues(self, name: AnyStr) -> Iterable[AnyStr]: ...
|
||||
def remove(self, name: String) -> None: ...
|
||||
def addValue(self, name: AnyStr, value: AnyStr) -> None: ...
|
34
typings/klein/_headers_compat.pyi
Normal file
34
typings/klein/_headers_compat.pyi
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import AnyStr, Iterable
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
from twisted.web.http_headers import Headers
|
||||
|
||||
from ._headers import IMutableHTTPHeaders, RawHeaders, String
|
||||
|
||||
"""
|
||||
Support for interoperability with L{twisted.web.http_headers.Headers}.
|
||||
"""
|
||||
__all__ = ()
|
||||
|
||||
@implementer(IMutableHTTPHeaders)
|
||||
@attrs(frozen=True)
|
||||
class HTTPHeadersWrappingHeaders:
|
||||
"""
|
||||
HTTP entity headers.
|
||||
|
||||
This is an L{IMutableHTTPHeaders} implementation that wraps a L{Headers}
|
||||
object.
|
||||
"""
|
||||
|
||||
_headers: Headers = ...
|
||||
@property
|
||||
def rawHeaders(self) -> RawHeaders: ...
|
||||
def getValues(self, name: AnyStr) -> Iterable[AnyStr]: ...
|
||||
def remove(self, name: String) -> None: ...
|
||||
def addValue(self, name: AnyStr, value: AnyStr) -> None: ...
|
21
typings/klein/_iform.pyi
Normal file
21
typings/klein/_iform.pyi
Normal file
|
@ -0,0 +1,21 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""
|
||||
A L{ValidationError} is raised by L{Field.extractValue}.
|
||||
"""
|
||||
|
||||
def __init__(self, message: object) -> None:
|
||||
"""
|
||||
Initialize a L{ValidationError} with a message to show to the user.
|
||||
"""
|
||||
...
|
||||
|
||||
class ValueAbsent(ValidationError):
|
||||
"""
|
||||
A value was required but none was supplied.
|
||||
"""
|
||||
|
||||
...
|
164
typings/klein/_imessage.pyi
Normal file
164
typings/klein/_imessage.pyi
Normal file
|
@ -0,0 +1,164 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import AnyStr, Iterable, MutableSequence, Sequence, Tuple
|
||||
|
||||
from zope.interface import Interface
|
||||
|
||||
from hyperlink import DecodedURL
|
||||
from tubes.itube import IFount
|
||||
|
||||
"""
|
||||
Interfaces related to HTTP messages.
|
||||
|
||||
Do not import directly from here, except:
|
||||
- From _interfaces.py.
|
||||
- From implementations of these interfaces, but even then, import the
|
||||
zope.interface.Interface classes via _interfaces.py.
|
||||
|
||||
This will ensure that type checking works.
|
||||
"""
|
||||
__all__ = ()
|
||||
RawHeader = Tuple[bytes, bytes]
|
||||
RawHeaders = Sequence[RawHeader]
|
||||
MutableRawHeaders = MutableSequence[RawHeader]
|
||||
|
||||
class FountAlreadyAccessedError(Exception):
|
||||
"""
|
||||
The HTTP message's fount has already been accessed and is no longer
|
||||
available.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
class IHTTPHeaders(Interface):
|
||||
"""
|
||||
HTTP entity headers.
|
||||
|
||||
HTTP headers names and values are sort-of-but-not-quite-specifically
|
||||
expected to be text.
|
||||
|
||||
Because the specifications are somewhat vague, and implementations vary in
|
||||
fidelity, both header names and values must be made available as the
|
||||
original bytes received from the network.
|
||||
This interface also makes them available as an ordered sequence of name and
|
||||
value pairs so that they can be iterated in the same order as they were
|
||||
received on the network.
|
||||
|
||||
As such, the C{rawHeaders} attribute provides the header data as a sequence
|
||||
of C{(name, value)} L{tuple}s.
|
||||
|
||||
A dictionary-like interface that maps text names to an ordered sequence of
|
||||
text values.
|
||||
This interface assumes that both header name bytes and header value bytes
|
||||
are encoded as ISO-8859-1.
|
||||
|
||||
Note that header name bytes should be strictly encoded as ASCII; this
|
||||
interface uses ISO-8859-1 to provide interoperability with (naughty) HTTP
|
||||
implementations that send non-ASCII data.
|
||||
Because ISO-8859-1 is a superset of ASCII, this will still work for
|
||||
well-behaved implementations.
|
||||
"""
|
||||
|
||||
rawHeaders: RawHeaders = ...
|
||||
def getValues(name: AnyStr) -> Iterable[AnyStr]:
|
||||
"""
|
||||
Get the values associated with the given header name.
|
||||
|
||||
If the given name is L{bytes}, the value will be returned as the raw
|
||||
header L{bytes}.
|
||||
|
||||
If the given name is L{str}, the name will be encoded as ISO-8859-1
|
||||
and the value will be returned as text, by decoding the raw header
|
||||
value bytes with ISO-8859-1.
|
||||
|
||||
@param name: The name of the header to look for.
|
||||
|
||||
@return: The values of the header with the given name.
|
||||
"""
|
||||
...
|
||||
|
||||
class IMutableHTTPHeaders(IHTTPHeaders):
|
||||
"""
|
||||
Mutable HTTP entity headers.
|
||||
"""
|
||||
|
||||
def remove(name: AnyStr) -> None:
|
||||
"""
|
||||
Remove all header name/value pairs for the given header name.
|
||||
|
||||
If the given name is L{str}, it will be encoded as ISO-8859-1 before
|
||||
comparing to the (L{bytes}) header names.
|
||||
|
||||
@param name: The name of the header to remove.
|
||||
"""
|
||||
...
|
||||
def addValue(name: AnyStr, value: AnyStr) -> None:
|
||||
"""
|
||||
Add the given header name/value pair.
|
||||
|
||||
If the given name is L{bytes}, the value must also be L{bytes}.
|
||||
|
||||
If the given name is L{str}, it will be encoded as ISO-8859-1, and the
|
||||
value, which must also be L{str}, will be encoded as ISO-8859-1.
|
||||
"""
|
||||
...
|
||||
|
||||
class IHTTPMessage(Interface):
|
||||
"""
|
||||
HTTP entity.
|
||||
"""
|
||||
|
||||
headers: IHTTPHeaders = ...
|
||||
def bodyAsFount() -> IFount:
|
||||
"""
|
||||
The entity body, as a fount.
|
||||
|
||||
@note: The fount may only be accessed once.
|
||||
It provides a mechanism for accessing the body as a stream of data,
|
||||
potentially as it is read from the network, without having to cache
|
||||
the entire body, which may be large.
|
||||
Because there is no caching, it is not possible to "start over" by
|
||||
accessing the fount a second time.
|
||||
Attempting to do so will raise L{FountAlreadyAccessedError}.
|
||||
|
||||
@raise FountAlreadyAccessedError: If the fount has previously been
|
||||
accessed.
|
||||
"""
|
||||
...
|
||||
async def bodyAsBytes() -> bytes:
|
||||
"""
|
||||
The entity body, as bytes.
|
||||
|
||||
@note: This necessarily reads the entire entity body into memory,
|
||||
which may be a problem if the body is large.
|
||||
|
||||
@note: This method caches the body, which means that unlike
|
||||
C{self.bodyAsFount}, calling it repeatedly will return the same
|
||||
data.
|
||||
|
||||
@note: This method accesses the fount (via C{self.bodyAsFount}), which
|
||||
means the fount will not be available afterwards, and that if
|
||||
C{self.bodyAsFount} has previously been called directly, this
|
||||
method will raise L{FountAlreadyAccessedError}.
|
||||
|
||||
@raise FountAlreadyAccessedError: If the fount has previously been
|
||||
accessed.
|
||||
"""
|
||||
...
|
||||
|
||||
class IHTTPRequest(IHTTPMessage):
|
||||
"""
|
||||
HTTP request.
|
||||
"""
|
||||
|
||||
method: str = ...
|
||||
uri: DecodedURL = ...
|
||||
|
||||
class IHTTPResponse(IHTTPMessage):
|
||||
"""
|
||||
HTTP response.
|
||||
"""
|
||||
|
||||
status: int = ...
|
32
typings/klein/_interfaces.pyi
Normal file
32
typings/klein/_interfaces.pyi
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Mapping, Optional, Union
|
||||
|
||||
from zope.interface import Interface
|
||||
|
||||
"""
|
||||
Internal interface definitions.
|
||||
|
||||
All Zope Interface classes should be imported from here so that type checking
|
||||
works, since mypy doesn't otherwise get along with Zope Interface.
|
||||
"""
|
||||
KleinQueryValue = Union[str, int, float]
|
||||
|
||||
class IKleinRequest(Interface):
|
||||
branch_segments = ...
|
||||
mapper = ...
|
||||
def url_for(
|
||||
endpoint: str,
|
||||
values: Optional[Mapping[str, KleinQueryValue]] = ...,
|
||||
method: Optional[str] = ...,
|
||||
force_external: bool = ...,
|
||||
append_unknown: bool = ...,
|
||||
) -> str:
|
||||
"""
|
||||
L{werkzeug.routing.MapAdapter.build}
|
||||
"""
|
||||
...
|
||||
|
||||
__all__ = ()
|
326
typings/klein/_isession.pyi
Normal file
326
typings/klein/_isession.pyi
Normal file
|
@ -0,0 +1,326 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Sequence, Type
|
||||
|
||||
from zope.interface import Interface
|
||||
|
||||
import attr
|
||||
from constantly import Names
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.python.components import Componentized
|
||||
from twisted.web.iweb import IRequest
|
||||
|
||||
from ._app import KleinRenderable
|
||||
|
||||
if TYPE_CHECKING: ...
|
||||
|
||||
class NoSuchSession(Exception):
|
||||
"""
|
||||
No such session could be found.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
class TooLateForCookies(Exception):
|
||||
"""
|
||||
It's too late to set a cookie.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
class TransactionEnded(Exception):
|
||||
"""
|
||||
Exception raised when.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
class SessionMechanism(Names):
|
||||
"""
|
||||
Mechanisms which can be used to identify and authenticate a session.
|
||||
|
||||
@cvar Cookie: The Cookie session mechanism involves looking up the session
|
||||
identifier via an HTTP cookie. Session objects retrieved via this
|
||||
mechanism may be vulnerable to U{CSRF attacks
|
||||
<https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)>}
|
||||
and therefore must have CSRF protections applied to them.
|
||||
|
||||
@cvar Header: The Header mechanism retrieves the session identifier via a
|
||||
separate header such as C{"X-Auth-Token"}. Since a different-origin
|
||||
site in a browser can easily send a form submission including cookies,
|
||||
but I{can't} easily put stuff into other arbitrary headers, this does
|
||||
not require additional protections.
|
||||
"""
|
||||
|
||||
Cookie = ...
|
||||
Header = ...
|
||||
|
||||
class ISession(Interface):
|
||||
"""
|
||||
An L{ISession} provider contains an identifier for the session, information
|
||||
about how the session was negotiated with the client software, and
|
||||
"""
|
||||
|
||||
identifier = ...
|
||||
isConfidential = ...
|
||||
authenticatedBy = ...
|
||||
def authorize(interfaces: Iterable[Type[Interface]]) -> Deferred:
|
||||
"""
|
||||
Retrieve other objects from this session.
|
||||
|
||||
This method is how you can retrieve application-specific objects from
|
||||
the general-purpose session; define interfaces for each facet of
|
||||
something accessible to a session, then pass it here and to the
|
||||
L{ISessionStore} implementation you're using.
|
||||
|
||||
@param interfaces: A list of interfaces.
|
||||
|
||||
@return: all of the providers that could be retrieved from the session.
|
||||
@rtype: L{Deferred} firing with L{dict} mapping
|
||||
L{zope.interface.interfaces.IInterface} to providers of each
|
||||
interface. Interfaces which cannot be authorized will not be
|
||||
present as keys in this dictionary.
|
||||
"""
|
||||
...
|
||||
|
||||
class ISessionStore(Interface):
|
||||
"""
|
||||
Backing storage for sessions.
|
||||
"""
|
||||
|
||||
def newSession(isConfidential: bool, authenticatedBy: SessionMechanism) -> Deferred:
|
||||
"""
|
||||
Create a new L{ISession}.
|
||||
|
||||
@return: a new session with a new identifier.
|
||||
@rtype: L{Deferred} firing with L{ISession}.
|
||||
"""
|
||||
...
|
||||
def loadSession(
|
||||
identifier: str, isConfidential: bool, authenticatedBy: SessionMechanism
|
||||
) -> Deferred:
|
||||
"""
|
||||
Load a session given the given identifier and security properties.
|
||||
|
||||
As an optimization for session stores where the back-end can generate
|
||||
session identifiers when the presented one is not found in the same
|
||||
round-trip to a data store, this method may return a L{Session} object
|
||||
with an C{identifier} attribute that does not match C{identifier}.
|
||||
However, please keep in mind when implementing L{ISessionStore} that
|
||||
this behavior is only necessary for requests where C{authenticatedBy}
|
||||
is L{SessionMechanism.Cookie}; an unauthenticated
|
||||
L{SessionMechanism.Header} session is from an API client and its
|
||||
session should be valid already.
|
||||
|
||||
@return: an existing session with the given identifier.
|
||||
@rtype: L{Deferred} firing with L{ISession} or failing with
|
||||
L{NoSuchSession}.
|
||||
"""
|
||||
...
|
||||
def sentInsecurely(identifiers: Sequence[str]) -> None:
|
||||
"""
|
||||
The transport layer has detected that the given identifiers have been
|
||||
sent over an unauthenticated transport.
|
||||
"""
|
||||
...
|
||||
|
||||
class ISimpleAccountBinding(Interface):
|
||||
"""
|
||||
Data-store agnostic account / session binding manipulation API for "simple"
|
||||
accounts - i.e. those using username, password, and email address as a
|
||||
method to authenticate a user.
|
||||
|
||||
This goes into a user-authentication-capable L{ISession} object's C{data}
|
||||
attribute as a component.
|
||||
"""
|
||||
|
||||
def bindIfCredentialsMatch(username: str, password: str) -> None:
|
||||
"""
|
||||
Attach the session this is a component of to an account with the given
|
||||
username and password, if the given username and password correctly
|
||||
authenticate a principal.
|
||||
"""
|
||||
...
|
||||
def boundAccounts() -> Deferred:
|
||||
"""
|
||||
Retrieve the accounts currently associated with the session this is a
|
||||
component of.
|
||||
|
||||
@return: L{Deferred} firing with a L{list} of L{ISimpleAccount}.
|
||||
"""
|
||||
...
|
||||
def unbindThisSession() -> None:
|
||||
"""
|
||||
Disassociate the session this is a component of from any accounts it's
|
||||
logged in to.
|
||||
"""
|
||||
...
|
||||
def createAccount(username: str, email: str, password: str) -> None:
|
||||
"""
|
||||
Create a new account with the given username, email and password.
|
||||
"""
|
||||
...
|
||||
|
||||
class ISimpleAccount(Interface):
|
||||
"""
|
||||
Data-store agnostic account interface.
|
||||
"""
|
||||
|
||||
username = ...
|
||||
accountID = ...
|
||||
def bindSession(session: ISession) -> None:
|
||||
"""
|
||||
Bind the given session to this account; i.e. authorize the given
|
||||
session to act on behalf of this account.
|
||||
"""
|
||||
...
|
||||
def changePassword(newPassword: str) -> None:
|
||||
"""
|
||||
Change the password of this account.
|
||||
"""
|
||||
...
|
||||
|
||||
class ISessionProcurer(Interface):
|
||||
"""
|
||||
An L{ISessionProcurer} wraps an L{ISessionStore} and can procure sessions
|
||||
that store, given HTTP request objects.
|
||||
"""
|
||||
|
||||
def procureSession(request: IRequest, forceInsecure: bool = ...) -> Deferred:
|
||||
"""
|
||||
Retrieve a session using whatever technique is necessary.
|
||||
|
||||
If the request already identifies an existing session in the store,
|
||||
retrieve it. If not, create a new session and retrieve that.
|
||||
|
||||
@param request: The request to procure a session from.
|
||||
@param forceInsecure: Even if the request was transmitted securely
|
||||
(i.e. over HTTPS), retrieve the session that would be used by the
|
||||
same browser if it were sending an insecure (i.e. over HTTP)
|
||||
request; by default, this is False, and the session's security will
|
||||
match that of the request.
|
||||
|
||||
@return: a L{Deferred} that:
|
||||
|
||||
- fires with an L{ISession} provider if the request describes
|
||||
an existing, valid session, or, if the intersection of the
|
||||
data in the request and the configuration of this
|
||||
L{ISessionProcurer} allow for a cookie to be set immediately,
|
||||
or
|
||||
|
||||
- fails with L{NoSuchSession} if the request is unable to
|
||||
negotiate a session based on the current request: this is
|
||||
generally if the client is trying to use header-based
|
||||
authentication (and therefore does not want a new cookie set)
|
||||
or if this procurer is configured not to automatically create
|
||||
new sessions on the fly, or
|
||||
|
||||
- fails with L{TooLateForCookies} if the request bound to this
|
||||
procurer has already sent the headers and therefore we can no
|
||||
longer set a cookie, and we need to set a cookie.
|
||||
"""
|
||||
...
|
||||
|
||||
class IDependencyInjector(Interface):
|
||||
"""
|
||||
An injector for a given dependency.
|
||||
"""
|
||||
|
||||
def injectValue(
|
||||
instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> Any:
|
||||
"""
|
||||
Return a value to be injected into the parameter name specified by the
|
||||
IRequiredParameter. This may return a Deferred, or an object, or an
|
||||
object directly providing the relevant interface.
|
||||
|
||||
@param instance: The instance to which the Klein router processing this
|
||||
request is bound.
|
||||
|
||||
@param request: The request being processed.
|
||||
|
||||
@param routeParams: A (read-only) copy of the the arguments passed to
|
||||
the route by the layer below dependency injection (for example, URL
|
||||
parameters).
|
||||
"""
|
||||
...
|
||||
def finalize() -> None:
|
||||
"""
|
||||
Finalize this injector before allowing the route to be created.
|
||||
|
||||
Finalization generally includes:
|
||||
|
||||
- attaching any hooks to the request lifecycle object that need to
|
||||
be run before/after each request
|
||||
|
||||
- attaching any finalized component objects to the
|
||||
injectionComponents originally passed along to the
|
||||
IRequiredParameter that created this IDependencyInjector.
|
||||
"""
|
||||
...
|
||||
|
||||
class IRequestLifecycle(Interface):
|
||||
"""
|
||||
Interface for adding hooks to the phases of a request's lifecycle.
|
||||
"""
|
||||
|
||||
def addPrepareHook(
|
||||
beforeHook: Callable,
|
||||
requires: Sequence[Type[Interface]] = ...,
|
||||
provides: Sequence[Type[Interface]] = ...,
|
||||
) -> None:
|
||||
"""
|
||||
Add a hook that promises to prepare the request by supplying the given
|
||||
interfaces as components on the request, and requires the given
|
||||
requirements.
|
||||
|
||||
Prepare hooks are run I{before any} L{IDependencyInjector}s I{inject
|
||||
their values}.
|
||||
"""
|
||||
...
|
||||
|
||||
class IRequiredParameter(Interface):
|
||||
"""
|
||||
A declaration that a given Python parameter is required to satisfy a given
|
||||
dependency at request-handling time.
|
||||
"""
|
||||
|
||||
def registerInjector(
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
lifecycle: IRequestLifecycle,
|
||||
) -> IDependencyInjector:
|
||||
"""
|
||||
Register the given injector at method-decoration time, informing it of
|
||||
its Python parameter name.
|
||||
|
||||
@note: this happens at I{route definition} time, after all other
|
||||
injectors have been registered by
|
||||
L{IRequiredParameter.registerInjector}.
|
||||
|
||||
@param lifecycle: An L{IRequestLifecycle} provider which contains hooks
|
||||
that will be run before and after each request. If this injector
|
||||
has shared per-request dependencies that need to be executed before
|
||||
or after the request is processed, this method should attach them
|
||||
to those lists. These hooks are supplied here rather than relying
|
||||
on C{injectValue} to run the requisite logic each time so that
|
||||
DependencyInjectors may cooperate on logic that needs to be
|
||||
duplicated, such as provisioning a session.
|
||||
"""
|
||||
...
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class EarlyExit(Exception):
|
||||
"""
|
||||
An L{EarlyExit} may be raised by any of the code that runs in the
|
||||
before-request dependency injection code path when using
|
||||
L{klein.Requirer.require}.
|
||||
|
||||
@ivar alternateReturnValue: The return value which should instead be
|
||||
supplied as the route's response.
|
||||
"""
|
||||
|
||||
alternateReturnValue: KleinRenderable
|
||||
...
|
41
typings/klein/_message.pyi
Normal file
41
typings/klein/_message.pyi
Normal file
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from attr import attrs
|
||||
from tubes.itube import IFount
|
||||
|
||||
"""
|
||||
HTTP message API.
|
||||
"""
|
||||
__all__ = ()
|
||||
InternalBody = Union[bytes, IFount]
|
||||
|
||||
@attrs(frozen=False)
|
||||
class MessageState:
|
||||
"""
|
||||
Internal mutable state for HTTP message implementations in L{klein}.
|
||||
"""
|
||||
|
||||
cachedBody: Optional[bytes] = ...
|
||||
fountExhausted: bool = ...
|
||||
|
||||
def validateBody(instance: Any, attribute: Any, body: InternalBody) -> None:
|
||||
"""
|
||||
Validator for L{InternalBody}.
|
||||
"""
|
||||
...
|
||||
|
||||
def bodyAsFount(body: InternalBody, state: MessageState) -> IFount:
|
||||
"""
|
||||
Return a fount for a given L{InternalBody}.
|
||||
"""
|
||||
...
|
||||
|
||||
async def bodyAsBytes(body: InternalBody, state: MessageState) -> bytes:
|
||||
"""
|
||||
Return bytes for a given L{InternalBody}.
|
||||
"""
|
||||
...
|
109
typings/klein/_plating.pyi
Normal file
109
typings/klein/_plating.pyi
Normal file
|
@ -0,0 +1,109 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Callable, Generator, List, Tuple
|
||||
|
||||
import attr
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
from twisted.web.template import Element
|
||||
|
||||
"""
|
||||
Templating wrapper support for Klein.
|
||||
"""
|
||||
StackType = List[Tuple[Any, Callable[[Any], None]]]
|
||||
ATOM_TYPES = ...
|
||||
|
||||
@inlineCallbacks
|
||||
def resolveDeferredObjects(root: Any) -> Generator[Any, object, Any]:
|
||||
"""
|
||||
Wait on possibly nested L{Deferred}s that represent a JSON
|
||||
serializable object.
|
||||
|
||||
@param root: JSON-serializable object that may contain
|
||||
L{Deferred}s that resolve to JSON-serializable objects, or a
|
||||
L{Deferred} that resolves to one.
|
||||
|
||||
@return: A L{Deferred} that fires with a L{Deferred}-free version
|
||||
of C{root}, or that fails with the first exception
|
||||
encountered.
|
||||
"""
|
||||
...
|
||||
|
||||
class PlatedElement(Element):
|
||||
"""
|
||||
The element type returned by L{Plating}. This contains several utility
|
||||
renderers.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, slot_data, preloaded, boundInstance, presentationSlots, renderers
|
||||
) -> None:
|
||||
"""
|
||||
@param slot_data: A dictionary mapping names to values.
|
||||
|
||||
@param preloaded: The pre-loaded data.
|
||||
"""
|
||||
...
|
||||
def lookupRenderMethod(
|
||||
self, name
|
||||
): # -> ((request: IRequest, tag: Tag, *args: Any, **kw: Any) -> Any) | ((request: Unknown, tag: Unknown) -> Generator[Unknown, None, None]):
|
||||
"""
|
||||
@return: a renderer.
|
||||
"""
|
||||
...
|
||||
|
||||
class Plating:
|
||||
"""
|
||||
A L{Plating} is a container which can be used to generate HTML from data.
|
||||
|
||||
Its name is derived both from tem-I{plating} and I{chrome plating}.
|
||||
"""
|
||||
|
||||
CONTENT = ...
|
||||
def __init__(self, defaults=..., tags=..., presentation_slots=...) -> None:
|
||||
""" """
|
||||
...
|
||||
def renderMethod(self, renderer):
|
||||
"""
|
||||
Add a render method to this L{Plating} object that can be used in the
|
||||
top-level template.
|
||||
|
||||
The name of the renderer to use within the template is the name of the
|
||||
decorated function.
|
||||
"""
|
||||
...
|
||||
def routed(self, routing, tags): # -> (method: Unknown) -> Unknown:
|
||||
""" """
|
||||
...
|
||||
@attr.s(auto_attribs=True)
|
||||
class _Widget:
|
||||
"""
|
||||
Implementation of L{Plating.widgeted}. This is a L{callable}
|
||||
descriptor that records the instance to which its wrapped
|
||||
function is bound, if any. Its L{widget} method then passes
|
||||
that instance or L{None} and the result of invoking the
|
||||
function (or now bound method) to the creating L{Plating}
|
||||
instance's L{Plating._elementify} to construct a
|
||||
L{PlatedElement}.
|
||||
"""
|
||||
|
||||
_plating: Plating
|
||||
_function: Callable[..., Any]
|
||||
_instance: object
|
||||
def __call__(self, *args, **kwargs): ...
|
||||
def __get__(self, instance, owner=...): ...
|
||||
def widget(self, *args, **kwargs): # -> PlatedElement:
|
||||
"""
|
||||
Construct a L{PlatedElement} the rendering of this widget.
|
||||
"""
|
||||
...
|
||||
def __getattr__(self, attr): ...
|
||||
|
||||
def widgeted(self, function): # -> _Widget:
|
||||
"""
|
||||
A decorator that turns a function into a renderer for an
|
||||
element without a L{Klein.route}. Use this to create reusable
|
||||
template elements.
|
||||
"""
|
||||
...
|
34
typings/klein/_request.pyi
Normal file
34
typings/klein/_request.pyi
Normal file
|
@ -0,0 +1,34 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
from hyperlink import DecodedURL
|
||||
from tubes.itube import IFount
|
||||
|
||||
from ._imessage import IHTTPHeaders, IHTTPRequest
|
||||
from ._message import MessageState
|
||||
|
||||
"""
|
||||
HTTP request API.
|
||||
"""
|
||||
__all__ = ()
|
||||
|
||||
@implementer(IHTTPRequest)
|
||||
@attrs(frozen=True)
|
||||
class FrozenHTTPRequest:
|
||||
"""
|
||||
Immutable HTTP request.
|
||||
"""
|
||||
|
||||
method: str = ...
|
||||
uri: DecodedURL = ...
|
||||
headers: IHTTPHeaders = ...
|
||||
_body: Union[bytes, IFount] = ...
|
||||
_state: MessageState = ...
|
||||
def bodyAsFount(self) -> IFount: ...
|
||||
async def bodyAsBytes(self) -> bytes: ...
|
40
typings/klein/_request_compat.pyi
Normal file
40
typings/klein/_request_compat.pyi
Normal file
|
@ -0,0 +1,40 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
from hyperlink import DecodedURL
|
||||
from tubes.itube import IFount
|
||||
from twisted.web.iweb import IRequest
|
||||
|
||||
from ._headers import IHTTPHeaders
|
||||
from ._message import MessageState
|
||||
from ._request import IHTTPRequest
|
||||
|
||||
"""
|
||||
Support for interoperability with L{twisted.web.iweb.IRequest}.
|
||||
"""
|
||||
__all__ = ()
|
||||
noneIO = ...
|
||||
|
||||
@implementer(IHTTPRequest)
|
||||
@attrs(frozen=True)
|
||||
class HTTPRequestWrappingIRequest:
|
||||
"""
|
||||
HTTP request.
|
||||
|
||||
This is an L{IHTTPRequest} implementation that wraps an L{IRequest} object.
|
||||
"""
|
||||
|
||||
_request: IRequest = ...
|
||||
_state: MessageState = ...
|
||||
@property
|
||||
def method(self) -> str: ...
|
||||
@property
|
||||
def uri(self) -> DecodedURL: ...
|
||||
@property
|
||||
def headers(self) -> IHTTPHeaders: ...
|
||||
def bodyAsFount(self) -> IFount: ...
|
||||
async def bodyAsBytes(self) -> bytes: ...
|
82
typings/klein/_requirer.pyi
Normal file
82
typings/klein/_requirer.pyi
Normal file
|
@ -0,0 +1,82 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Callable, Generator, List, Sequence, Type
|
||||
|
||||
from zope.interface import Interface, implementer
|
||||
|
||||
import attr
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
from twisted.web.iweb import IRequest
|
||||
|
||||
from .interfaces import IRequestLifecycle, IRequiredParameter
|
||||
|
||||
@implementer(IRequestLifecycle)
|
||||
@attr.s(auto_attribs=True)
|
||||
class RequestLifecycle:
|
||||
"""
|
||||
Mechanism to run hooks at the start of a request managed by a L{Requirer}.
|
||||
"""
|
||||
|
||||
_prepareHooks: List = ...
|
||||
def addPrepareHook(
|
||||
self,
|
||||
beforeHook: Callable,
|
||||
requires: Sequence[Type[Interface]] = ...,
|
||||
provides: Sequence[Type[Interface]] = ...,
|
||||
) -> None: ...
|
||||
@inlineCallbacks
|
||||
def runPrepareHooks(
|
||||
self, instance: Any, request: IRequest
|
||||
) -> Generator[Any, object, None]:
|
||||
"""
|
||||
Execute all the hooks added with L{RequestLifecycle.addPrepareHook}.
|
||||
This is invoked by the L{requires} route machinery.
|
||||
|
||||
@param instance: The instance bound to the Klein route.
|
||||
|
||||
@param request: The IRequest being processed.
|
||||
"""
|
||||
...
|
||||
|
||||
_routeDecorator = Any
|
||||
_routeT = Any
|
||||
_prerequisiteCallback = Callable[[IRequestLifecycle], None]
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Requirer:
|
||||
"""
|
||||
Dependency injection for required parameters.
|
||||
"""
|
||||
|
||||
_prerequisites: List[_prerequisiteCallback] = ...
|
||||
def prerequisite(
|
||||
self,
|
||||
providesComponents: Sequence[Type[Interface]],
|
||||
requiresComponents: Sequence[Type[Interface]] = ...,
|
||||
) -> Callable[[Callable], Callable]:
|
||||
"""
|
||||
Specify a component that is a pre-requisite of every request routed
|
||||
through this requirer's C{require} method. Used like so::
|
||||
|
||||
requirer = Requirer()
|
||||
|
||||
@requirer.prerequisite([IFoo])
|
||||
@inlineCallbacks
|
||||
def fooForRequest(request):
|
||||
request.setComponent(IFoo, someFooComponent)
|
||||
|
||||
@note: C{requiresComponents} is, at this point, for the reader's
|
||||
interest only, the framework will not topologically sort
|
||||
dependencies; you must presently register prerequisites in the
|
||||
order you want them to be called.
|
||||
"""
|
||||
...
|
||||
def require(
|
||||
self, routeDecorator: _routeT, **requiredParameters: IRequiredParameter
|
||||
) -> _routeDecorator:
|
||||
"""
|
||||
Inject the given dependencies while running the given route.
|
||||
"""
|
||||
...
|
72
typings/klein/_resource.pyi
Normal file
72
typings/klein/_resource.pyi
Normal file
|
@ -0,0 +1,72 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Sequence, Tuple, Union
|
||||
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.web.iweb import IRequest
|
||||
from twisted.web.resource import Resource
|
||||
|
||||
from ._app import Klein, KleinRenderable
|
||||
|
||||
if TYPE_CHECKING: ...
|
||||
|
||||
def ensure_utf8_bytes(v: Union[str, bytes]) -> bytes:
|
||||
"""
|
||||
Coerces a value which is either a C{str} or C{bytes} to a C{bytes}.
|
||||
If ``v`` is a C{str} object it is encoded as utf-8.
|
||||
"""
|
||||
...
|
||||
|
||||
class _StandInResource:
|
||||
"""
|
||||
A standin for a Resource.
|
||||
|
||||
This is a sentinel value for L{KleinResource}, to say that we are rendering
|
||||
a L{Resource}, which may close the connection itself later.
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
StandInResource = ...
|
||||
|
||||
class URLDecodeError(Exception):
|
||||
"""
|
||||
Raised if one or more string parts of the URL could not be decoded.
|
||||
"""
|
||||
|
||||
__slots__ = ...
|
||||
def __init__(self, errors: Sequence[Tuple[str, Failure]]) -> None:
|
||||
"""
|
||||
@param errors: Sequence of decoding errors, expressed as tuples
|
||||
of names and an associated failure.
|
||||
"""
|
||||
...
|
||||
def __repr__(self) -> str: ...
|
||||
|
||||
def extractURLparts(request: IRequest) -> Tuple[str, str, int, str, str]:
|
||||
"""
|
||||
Extracts and decodes URI parts from C{request}.
|
||||
|
||||
All strings must be UTF8-decodable.
|
||||
|
||||
@param request: A Twisted Web request.
|
||||
|
||||
@raise URLDecodeError: If one of the parts could not be decoded as UTF-8.
|
||||
|
||||
@return: L{tuple} of the URL scheme, the server name, the server port, the
|
||||
path info and the script name.
|
||||
"""
|
||||
...
|
||||
|
||||
class KleinResource(Resource):
|
||||
"""
|
||||
A ``Resource`` that can do URL routing.
|
||||
"""
|
||||
|
||||
isLeaf = ...
|
||||
def __init__(self, app: Klein) -> None: ...
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __ne__(self, other: object) -> bool: ...
|
||||
def render(self, request: IRequest) -> KleinRenderable: ...
|
32
typings/klein/_response.pyi
Normal file
32
typings/klein/_response.pyi
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
from tubes.itube import IFount
|
||||
|
||||
from ._imessage import IHTTPHeaders, IHTTPResponse
|
||||
from ._message import MessageState
|
||||
|
||||
"""
|
||||
HTTP response API.
|
||||
"""
|
||||
__all__ = ()
|
||||
|
||||
@implementer(IHTTPResponse)
|
||||
@attrs(frozen=True)
|
||||
class FrozenHTTPResponse:
|
||||
"""
|
||||
Immutable HTTP response.
|
||||
"""
|
||||
|
||||
status: int = ...
|
||||
headers: IHTTPHeaders = ...
|
||||
_body: Union[bytes, IFount] = ...
|
||||
_state: MessageState = ...
|
||||
def bodyAsFount(self) -> IFount: ...
|
||||
async def bodyAsBytes(self) -> bytes: ...
|
145
typings/klein/_session.pyi
Normal file
145
typings/klein/_session.pyi
Normal file
|
@ -0,0 +1,145 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Callable, Dict, Optional, Type
|
||||
|
||||
from zope.interface import Interface, implementer
|
||||
|
||||
import attr
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
from twisted.python.components import Componentized
|
||||
from twisted.web.iweb import IRequest
|
||||
from twisted.web.resource import Resource
|
||||
|
||||
from .interfaces import (
|
||||
IDependencyInjector,
|
||||
IRequestLifecycle,
|
||||
IRequiredParameter,
|
||||
ISessionProcurer,
|
||||
ISessionStore,
|
||||
)
|
||||
|
||||
@implementer(ISessionProcurer)
|
||||
@attr.s(auto_attribs=True)
|
||||
class SessionProcurer:
|
||||
"""
|
||||
A L{SessionProcurer} procures a session from a request and a store.
|
||||
|
||||
@ivar _store: The session store to procure a session from.
|
||||
@ivar _maxAge: The maximum age (in seconds) of the session cookie.
|
||||
@ivar _secureCookie: The name of the cookie to use for sessions protected
|
||||
with TLS (i.e. HTTPS).
|
||||
@ivar _insecureCookie: The name of the cookie to use for sessions I{not}
|
||||
protected with TLS (i.e. HTTP).
|
||||
@ivar _cookieDomain: If set, the domain name to restrict the session cookie
|
||||
to.
|
||||
@ivar _cookiePath: If set, the URL path to restrict the session cookie to.
|
||||
@ivar _secureTokenHeader: The name of the HTTPS header to try to extract a
|
||||
session token from; API clients should use this header, rather than a
|
||||
cookie.
|
||||
@ivar _insecureTokenHeader: The name of the HTTP header to try to extract a
|
||||
session token from; API clients should use this header, rather than a
|
||||
cookie.
|
||||
@ivar _setCookieOnGET: Automatically request that the session store create
|
||||
a session if one is not already associated with the request and the
|
||||
request is a GET.
|
||||
"""
|
||||
|
||||
_store: ISessionStore
|
||||
_maxAge: int = ...
|
||||
_secureCookie: bytes = ...
|
||||
_insecureCookie: bytes = ...
|
||||
_cookieDomain: Optional[bytes] = ...
|
||||
_cookiePath: bytes = ...
|
||||
_secureTokenHeader: bytes = ...
|
||||
_insecureTokenHeader: bytes = ...
|
||||
_setCookieOnGET: bool = ...
|
||||
@inlineCallbacks
|
||||
def procureSession(self, request: IRequest, forceInsecure: bool = ...) -> Any: ...
|
||||
|
||||
class AuthorizationDenied(Resource):
|
||||
def __init__(self, interface: Type[Interface], instance: Any) -> None: ...
|
||||
def render(self, request: IRequest) -> bytes: ...
|
||||
|
||||
@implementer(IDependencyInjector, IRequiredParameter)
|
||||
@attr.s(auto_attribs=True)
|
||||
class Authorization:
|
||||
"""
|
||||
Declare that a C{require}-decorated function requires a certain interface
|
||||
be authorized from the session.
|
||||
|
||||
This is a dependency injector used in conjunction with a L{klein.Requirer},
|
||||
like so::
|
||||
|
||||
from klein import Requirer, SesssionProcurer
|
||||
from klein.interfaces import ISession
|
||||
|
||||
from myapp import ISuperDuperAdmin
|
||||
|
||||
requirer = Requirer()
|
||||
procurer = SessionProcurer(store=someSessionStore)
|
||||
@requirer.prerequisite(ISession)
|
||||
def sessionize(request):
|
||||
return procurer.procureSession(request)
|
||||
|
||||
app = Klein()
|
||||
|
||||
@requirer.require(
|
||||
app.route("/admin"),
|
||||
adminPowers=Authorization(ISuperDuperAdmin)
|
||||
)
|
||||
def myRoute(adminPowers):
|
||||
return 'ok admin: ' + adminPowers.doAdminThing()
|
||||
|
||||
In this example, ISuperDuperAdmin is an interface known to your
|
||||
application, and (via authorization plugins depending on your session
|
||||
storage backend) to your session store. It has a doAdminThing method.
|
||||
When a user hits /admin in their browser, if they are duly authorized,
|
||||
they'll see 'ok admin: ' and whatever the super-secret result of
|
||||
doAdminThing is. If not, by default, they'll simply get an HTTP
|
||||
UNAUTHORIZED response that says "myapp.ISuperDuperAdmin DENIED". (This
|
||||
behavior can be customized via the C{whenDenied} parameter to
|
||||
L{Authorization}.)
|
||||
|
||||
@ivar _interface: the interface that is required. a provider of this
|
||||
interface is what will be dependency-injected.
|
||||
|
||||
@ivar _required: is this authorization required? If so (the default),
|
||||
don't invoke the application code if it cannot be authorized by the
|
||||
procured session, and instead return the object specified by whenDenied
|
||||
from the dependency-injection process. If not, then just pass None if
|
||||
it is not on the session.
|
||||
|
||||
@ivar _whenDenied: when this authorization is denied, what object - usually
|
||||
an IResource - should be returned to the route decorator that was
|
||||
passed to L{Requirer.require}? Note that this will never be used if
|
||||
C{required} is set to C{False}.
|
||||
"""
|
||||
|
||||
_interface: Type[Interface]
|
||||
_required: bool = ...
|
||||
_whenDenied: Callable[[Type[Interface], Any], Any] = ...
|
||||
def registerInjector(
|
||||
self,
|
||||
injectionComponents: Componentized,
|
||||
parameterName: str,
|
||||
lifecycle: IRequestLifecycle,
|
||||
) -> IDependencyInjector:
|
||||
"""
|
||||
Register this authorization to inject a parameter.
|
||||
"""
|
||||
...
|
||||
@inlineCallbacks
|
||||
def injectValue(
|
||||
self, instance: Any, request: IRequest, routeParams: Dict[str, Any]
|
||||
) -> Any:
|
||||
"""
|
||||
Inject a value by asking the request's session.
|
||||
"""
|
||||
...
|
||||
def finalize(self) -> None:
|
||||
"""
|
||||
Nothing to finalize when registering.
|
||||
"""
|
||||
...
|
33
typings/klein/_tubes.pyi
Normal file
33
typings/klein/_tubes.pyi
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, BinaryIO
|
||||
|
||||
from zope.interface import implementer
|
||||
|
||||
from attr import attrs
|
||||
from tubes.itube import IDrain, IFount, ISegment
|
||||
|
||||
"""
|
||||
Extensions to Tubes.
|
||||
"""
|
||||
__all__ = ()
|
||||
|
||||
async def fountToBytes(fount: IFount) -> bytes: ...
|
||||
def bytesToFount(data: bytes) -> IFount: ...
|
||||
@implementer(IFount)
|
||||
@attrs(frozen=False)
|
||||
class IOFount:
|
||||
"""
|
||||
Fount that reads from a file-like-object.
|
||||
"""
|
||||
|
||||
outputType = ISegment
|
||||
_source: BinaryIO = ...
|
||||
drain: IDrain = ...
|
||||
_paused = ...
|
||||
def __attrs_post_init__(self) -> None: ...
|
||||
def flowTo(self, drain: IDrain) -> IFount: ...
|
||||
def pauseFlow(self) -> Any: ...
|
||||
def stopFlow(self) -> Any: ...
|
9
typings/klein/_version.pyi
Normal file
9
typings/klein/_version.pyi
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
"""
|
||||
Provides klein version information.
|
||||
"""
|
||||
__version__ = ...
|
||||
__all__ = ["__version__"]
|
15
typings/klein/app.pyi
Normal file
15
typings/klein/app.pyi
Normal file
|
@ -0,0 +1,15 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"Klein",
|
||||
"KleinRequest",
|
||||
"handle_errors",
|
||||
"resource",
|
||||
"route",
|
||||
"run",
|
||||
"subroute",
|
||||
"urlFor",
|
||||
"url_for",
|
||||
)
|
22
typings/klein/interfaces.pyi
Normal file
22
typings/klein/interfaces.pyi
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"EarlyExit",
|
||||
"IDependencyInjector",
|
||||
"IKleinRequest",
|
||||
"IRequestLifecycle",
|
||||
"IRequiredParameter",
|
||||
"ISession",
|
||||
"ISessionProcurer",
|
||||
"ISessionStore",
|
||||
"ISimpleAccount",
|
||||
"ISimpleAccountBinding",
|
||||
"NoSuchSession",
|
||||
"SessionMechanism",
|
||||
"TooLateForCookies",
|
||||
"TransactionEnded",
|
||||
"ValidationError",
|
||||
"ValueAbsent",
|
||||
)
|
44
typings/klein/resource.pyi
Normal file
44
typings/klein/resource.pyi
Normal file
|
@ -0,0 +1,44 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from typing import Any, Callable, Union
|
||||
|
||||
from ._resource import KleinResource as _KleinResource
|
||||
|
||||
"""
|
||||
This module, L{klein.resource}, serves two purposes:
|
||||
|
||||
- It's the global C{resource()} method on the global L{klein.Klein}
|
||||
application.
|
||||
|
||||
- It's the module where L{KleinResource} is defined.
|
||||
"""
|
||||
KleinResource = _KleinResource
|
||||
|
||||
class _SpecialModuleObject:
|
||||
"""
|
||||
See the test in
|
||||
L{klein.test.test_resource.GlobalAppTests.test_weird_resource_situation}
|
||||
for an explanation.
|
||||
"""
|
||||
|
||||
__all__ = ("KleinResource", "ensure_utf8_bytes")
|
||||
KleinResource = _KleinResource
|
||||
def __init__(self, preserve: Any) -> None: ...
|
||||
@property
|
||||
def ensure_utf8_bytes(self) -> Callable[[Union[str, bytes]], bytes]: ...
|
||||
def __call__(self) -> _KleinResource:
|
||||
"""
|
||||
Return an L{IResource} which suitably wraps this app.
|
||||
|
||||
@returns: An L{IResource}
|
||||
"""
|
||||
...
|
||||
def __repr__(self) -> str:
|
||||
"""
|
||||
Give a special C{repr()} to make the dual purpose of this object clear.
|
||||
"""
|
||||
...
|
||||
|
||||
module = ...
|
3
typings/klein/storage/__init__.pyi
Normal file
3
typings/klein/storage/__init__.pyi
Normal file
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
9
typings/klein/test/__init__.pyi
Normal file
9
typings/klein/test/__init__.pyi
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""
|
||||
This type stub file was generated by pyright.
|
||||
"""
|
||||
|
||||
from hypothesis import HealthCheck, settings
|
||||
|
||||
"""
|
||||
Tests for L{klein}.
|
||||
"""
|
130
typings/twisted/python/filepath.pyi
Normal file
130
typings/twisted/python/filepath.pyi
Normal file
|
@ -0,0 +1,130 @@
|
|||
import base64
|
||||
import os
|
||||
from collections.abc import Generator
|
||||
from typing import IO, Iterable, Tuple, Union
|
||||
|
||||
from zope.interface import Interface
|
||||
|
||||
from _typeshed import Incomplete
|
||||
from twisted.python.compat import cmp as cmp, comparable as comparable
|
||||
from twisted.python.runtime import platform as platform
|
||||
from twisted.python.util import FancyEqMixin as FancyEqMixin
|
||||
from twisted.python.win32 import (
|
||||
ERROR_DIRECTORY as ERROR_DIRECTORY,
|
||||
ERROR_FILE_NOT_FOUND as ERROR_FILE_NOT_FOUND,
|
||||
ERROR_INVALID_NAME as ERROR_INVALID_NAME,
|
||||
ERROR_PATH_NOT_FOUND as ERROR_PATH_NOT_FOUND,
|
||||
O_BINARY as O_BINARY,
|
||||
)
|
||||
|
||||
islink: Incomplete
|
||||
randomBytes = os.urandom
|
||||
armor = base64.urlsafe_b64encode
|
||||
|
||||
class IFilePath(Interface):
|
||||
sep: Incomplete
|
||||
def child(name) -> None: ...
|
||||
def open(mode: str = ...) -> None: ...
|
||||
def changed() -> None: ...
|
||||
def getsize() -> None: ...
|
||||
def getModificationTime() -> None: ...
|
||||
def getStatusChangeTime() -> None: ...
|
||||
def getAccessTime() -> None: ...
|
||||
def exists() -> bool: ...
|
||||
def isdir() -> bool: ...
|
||||
def isfile() -> bool: ...
|
||||
def children() -> Iterable[IFilePath]: ...
|
||||
def basename() -> None: ...
|
||||
def parent() -> IFilePath: ...
|
||||
def sibling(name) -> None: ...
|
||||
|
||||
class InsecurePath(Exception): ...
|
||||
class LinkError(Exception): ...
|
||||
|
||||
class UnlistableError(OSError):
|
||||
originalException: Incomplete
|
||||
def __init__(self, originalException: OSError) -> None: ...
|
||||
|
||||
class AbstractFilePath:
|
||||
def getContent(self) -> bytes: ...
|
||||
def parents(self) -> Generator[Incomplete, None, None]: ...
|
||||
def children(self) -> Iterable[IFilePath]: ...
|
||||
def walk(
|
||||
self, descend: Incomplete | None = ...
|
||||
) -> Generator[Incomplete, None, None]: ...
|
||||
def sibling(self, path): ...
|
||||
def descendant(self, segments): ...
|
||||
def segmentsFrom(self, ancestor): ...
|
||||
def __hash__(self): ...
|
||||
def getmtime(self): ...
|
||||
def getatime(self): ...
|
||||
def getctime(self): ...
|
||||
|
||||
class RWX(FancyEqMixin):
|
||||
compareAttributes: Incomplete
|
||||
read: Incomplete
|
||||
write: Incomplete
|
||||
execute: Incomplete
|
||||
def __init__(self, readable, writable, executable) -> None: ...
|
||||
def shorthand(self): ...
|
||||
|
||||
class Permissions(FancyEqMixin):
|
||||
compareAttributes: Incomplete
|
||||
def __init__(self, statModeInt) -> None: ...
|
||||
def shorthand(self): ...
|
||||
|
||||
class FilePath(AbstractFilePath):
|
||||
path: Union[bytes, str]
|
||||
alwaysCreate: Incomplete
|
||||
def __init__(self, path: Union[bytes, str], alwaysCreate: bool = ...) -> None: ...
|
||||
@property
|
||||
def sep(self): ...
|
||||
def asBytesMode(self, encoding: Incomplete | None = ...): ...
|
||||
def asTextMode(self, encoding: Incomplete | None = ...): ...
|
||||
def child(self, path: Union[str, bytes]) -> FilePath: ...
|
||||
def preauthChild(self, path): ...
|
||||
def childSearchPreauth(self, *paths): ...
|
||||
def siblingExtensionSearch(self, *exts) -> FilePath: ...
|
||||
def realpath(self): ...
|
||||
def siblingExtension(self, ext: Union[str, bytes]) -> FilePath: ...
|
||||
def linkTo(self, linkFilePath) -> None: ...
|
||||
def open(self, mode: str = ...) -> IO[bytes]: ...
|
||||
def restat(self, reraise: bool = ...) -> None: ...
|
||||
def changed(self) -> None: ...
|
||||
def chmod(self, mode: int) -> None: ...
|
||||
def getsize(self): ...
|
||||
def getModificationTime(self): ...
|
||||
def getStatusChangeTime(self): ...
|
||||
def getAccessTime(self): ...
|
||||
def getInodeNumber(self): ...
|
||||
def getDevice(self): ...
|
||||
def getNumberOfHardLinks(self): ...
|
||||
def getUserID(self): ...
|
||||
def getGroupID(self): ...
|
||||
def getPermissions(self): ...
|
||||
def exists(self) -> bool: ...
|
||||
def isdir(self) -> bool: ...
|
||||
def isfile(self) -> bool: ...
|
||||
def isBlockDevice(self): ...
|
||||
def isSocket(self): ...
|
||||
def islink(self): ...
|
||||
def isabs(self): ...
|
||||
def listdir(self): ...
|
||||
def splitext(self) -> Tuple[str, str]: ...
|
||||
def touch(self) -> None: ...
|
||||
def remove(self) -> None: ...
|
||||
def makedirs(self, ignoreExistingDirectory: bool = ...) -> None: ...
|
||||
def globChildren(self, pattern): ...
|
||||
def basename(self) -> str: ...
|
||||
def dirname(self): ...
|
||||
def parent(self) -> FilePath: ...
|
||||
def setContent(self, content: bytes, ext: bytes = ...) -> None: ...
|
||||
def __cmp__(self, other): ...
|
||||
def createDirectory(self) -> None: ...
|
||||
def requireCreate(self, val: int = ...) -> None: ...
|
||||
def create(self) -> IO[bytes]: ...
|
||||
def temporarySibling(self, extension: bytes = ...) -> FilePath: ...
|
||||
def copyTo(self, destination, followLinks: bool = ...) -> None: ...
|
||||
def moveTo(
|
||||
self, destination: Union[str, bytes, FilePath], followLinks: bool = ...
|
||||
) -> None: ...
|
Loading…
Reference in a new issue