Type annotations: Pass-through questions
tony opened this issue · comments
Where do type annotations really stand?
PEP 612 talks about param specifications, but in reality - it's barely tangential to it, it's apparently syntactic decorators - an narrow, peripheral edge case.
How do you annotate pass-through params on class methods to third party libraries
I do not understand how param passthrough works with Python annotations (as of Python 3.10).
Lay of the land
Python has various forms of callback annotations.
Analogue in TypeScript / React
If you're familiar with TypeScript and React, I'm essentially expecting behavior like (inputProps: Partial<React.HTMLProps<HTMLInputElement>>) => <input {...inputProps} />
(sandbox: https://codesandbox.io/s/react-tsx-param-passthrough-73t8m8)
import React from "react";
const Button: React.FC<
Partial<Omit<React.HTMLProps<HTMLButtonElement>, "type">>
> = ({ children, ...rest }) => <button {...rest}>{children}</button>;
export default function App() {
return (
<div>
<Button onClick={() => alert("test")}>Click me</Button>
{/* completions in <Button> show enriched from HTMLInputElement */}
</div>
);
}
Example: REST Client
This example class will wrap a third party library and allow passing through any positional or keyword arguments optionally via *args
and **kwargs
.
Let's assume RestClient
, an authenticated REST client built on requests
.
Authentication works through requests.auth.Base
being instantiated and passed into auth
params, e.g. requests.request(method, url, auth=self.auth)
Methods will map to API endpoints, e.g. client.list_items()
-> GET {API_URL}/items
.
Also, custom authenticated requests will be made possible directly through client.{post,put,patch,get,delete}
. These map respectively to requests.{post,put,get,delete,patch}(auth=self.auth, *args, **kwargs)
.
Specs
- Use
requests
+ its [typeshed typings](https://github.com/python/typeshed/tree/51f97dd/stubs/requests - test the examples
- Make them succinct and minimal / well-explained as possible
Code drafts
import requests
from typing import Literal, Union
HttpMethodLiteral = Union[
Literal["GET"],
Literal["POST"],
Literal["PUT"],
Literal["DELETE"],
Literal["OPTIONS"],
]
Example 1: Can get()'s kwargs and response inherit from request()'s?
def request(url: str, method: HttpMethodLiteral) -> requests.Response:
if method == "GET":
return requests.get(url)
elif method == "POST":
return requests.post(url)
elif method == "PUT":
return requests.put(url)
elif method == "DELETE":
return requests.delete(url)
elif method == "OPTIONS":
return requests.options(url)
def get(url:str, **kwargs):
return requests(url=url, method='GET', **kwargs)
Example 2:
def request_get(url: str, method: HttpMethodLiteral, **kwargs) -> requests.Response:
if method == "GET":
return requests.get(url, **kwargs)
Example 3: Can request_passthrough()
's kwargs access requests.get
, requests.put
, etc.
def request_passthrough(url: str, method: HttpMethodLiteral, **kwargs) -> requests.Response:
if method == "GET":
return requests.get(url, **kwargs)
elif method == "POST":
return requests.post(url, **kwargs)
elif method == "PUT":
return requests.put(url, **kwargs)
elif method == "DELETE":
return requests.delete(url, **kwargs)
elif method == "OPTIONS":
return requests.options(url, **kwargs)
Example 4: If Example 2 is possible, can get()
's kwargs ...