Compare commits
27 Commits
pre-releas
...
v0.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ae4712c29 | ||
|
|
129b42613d | ||
|
|
f917b60ea0 | ||
|
|
7882598922 | ||
|
|
85c818539a | ||
|
|
78010eafcf | ||
|
|
8363b315c3 | ||
|
|
976c7e580f | ||
|
|
c66e6cc66b | ||
|
|
cadd4889b6 | ||
|
|
1d92b331e2 | ||
|
|
c740884f5f | ||
|
|
58f9117df6 | ||
|
|
aa1684b9c4 | ||
|
|
115f8c42ec | ||
|
|
2b9d0e1212 | ||
|
|
4688b5bfd9 | ||
|
|
892b68f0aa | ||
|
|
9b2902e747 | ||
|
|
3bcfa1bd1c | ||
|
|
afe03f72f0 | ||
|
|
3f8d2bb108 | ||
|
|
0efe5c901e | ||
|
|
561b4db23b | ||
|
|
34a6e3b2f1 | ||
|
|
4b32c0133c | ||
|
|
a57d0e9efe |
39
.github/workflows/tests.yml
vendored
Normal file
39
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
branches: ["master"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install flake8 pytest
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
- name: Lint with flake8
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
pytest
|
||||
21
LICENCE.txt
Normal file
21
LICENCE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
47
README.md
47
README.md
@@ -1,5 +1,46 @@
|
||||
# pocketbase
|
||||
# PocketBase Python SDK
|
||||
|
||||
Python client for PocketBase database.
|
||||
[](https://github.com/vaphes/pocketbase/actions/workflows/tests.yml)
|
||||
|
||||
This is in early development, and at first is just a translations for the javascript lib.
|
||||
Python client SDK for the <a href="https://pocketbase.io/">PocketBase</a> backend.
|
||||
|
||||
This is in early development, and at first is just a translation of <a href="https://github.com/pocketbase/js-sdk">the javascript lib</a> using <a href="https://github.com/encode/httpx/">HTTPX</a>.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Install PocketBase using pip:
|
||||
|
||||
```shell
|
||||
$ pip install pocketbase
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The rule of thumb here is just to use it as you would <a href="https://github.com/pocketbase/js-sdk">the javascript lib</a>, but in a pythonic way of course!
|
||||
|
||||
```python
|
||||
from pocketbase import PocketBase # Client also works the same
|
||||
|
||||
client = PocketBase('http://127.0.0.1:8090')
|
||||
|
||||
...
|
||||
|
||||
# list and filter "example" collection records
|
||||
result = client.records.get_list(
|
||||
"example", 1, 20, {"filter": 'status = true && created > "2022-08-01 10:00:00"'}
|
||||
)
|
||||
|
||||
# authenticate as regular user
|
||||
user_data = client.users.auth_via_email("test@example.com", "123456")
|
||||
|
||||
# or as admin
|
||||
admin_data = client.admins.auth_via_email("test@example.com", "123456")
|
||||
|
||||
# and much more...
|
||||
```
|
||||
> More detailed API docs and copy-paste examples could be found in the [API documentation for each service](https://pocketbase.io/docs/api-authentication). Just remember to 'pythonize it' 🙃.
|
||||
|
||||
|
||||
<p align="center"><i>The PocketBase Python SDK is <a href="https://github.com/vaphes/pocketbase/blob/master/LICENCE.txt">MIT licensed</a> code.</p>
|
||||
@@ -1,3 +1,17 @@
|
||||
__version__ = "0.1.0"
|
||||
__title__ = "pocketbase"
|
||||
__description__ = "PocketBase client SDK for python."
|
||||
__version__ = "0.2.2"
|
||||
|
||||
|
||||
from .client import Client, ClientResponseError
|
||||
|
||||
|
||||
class PocketBase(Client):
|
||||
"""
|
||||
Proxy class for `Client`
|
||||
|
||||
This is for cosmetic reasons only as you can use the
|
||||
`Client` class just the same
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -12,15 +13,13 @@ from pocketbase.services.users import Users
|
||||
from pocketbase.services.settings import Settings
|
||||
from pocketbase.stores.base_auth_store import BaseAuthStore
|
||||
|
||||
# from pocketbase.stores.local_auth_store import LocalAuthStore
|
||||
|
||||
|
||||
class ClientResponseError(Exception):
|
||||
url: str = ""
|
||||
status: int = 0
|
||||
data: dict = {}
|
||||
is_abort: bool = False
|
||||
original_error: Any = None
|
||||
original_error: Any | None = None
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args)
|
||||
@@ -47,7 +46,7 @@ class Client:
|
||||
self,
|
||||
base_url: str = "/",
|
||||
lang: str = "en-US",
|
||||
auth_store: BaseAuthStore = None,
|
||||
auth_store: BaseAuthStore | None = None,
|
||||
) -> None:
|
||||
self.base_url = base_url
|
||||
self.lang = lang
|
||||
@@ -61,19 +60,13 @@ class Client:
|
||||
self.settings = Settings(self)
|
||||
self.realtime = Realtime(self)
|
||||
|
||||
def cancel_request(self, cancel_key: str):
|
||||
return self
|
||||
|
||||
def cancel_all_requests(self):
|
||||
return self
|
||||
|
||||
def send(self, path: str, req_config: dict[str:Any]) -> Any:
|
||||
"""Sends an api http request."""
|
||||
config = {"method": "GET"}
|
||||
config.update(req_config)
|
||||
# check if Authorization header can be added
|
||||
if self.auth_store.token and (
|
||||
not "headers" in config or "Authorization" not in config["headers"]
|
||||
"headers" not in config or "Authorization" not in config["headers"]
|
||||
):
|
||||
auth_type = "Admin"
|
||||
if hasattr(self.auth_store.model, "verified"):
|
||||
@@ -123,22 +116,3 @@ class Client:
|
||||
if path.startswith("/"):
|
||||
path = path[1:]
|
||||
return url + path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from pocketbase.stores.local_auth_store import LocalAuthStore
|
||||
|
||||
pb = Client(base_url="http://ares.olimpo:8090/", auth_store=LocalAuthStore())
|
||||
# pb.admins.auth_via_email("vaphes@gmail.com", "vaphes2007")
|
||||
print(pb.auth_store.token)
|
||||
books = pb.collections.get_one("books")
|
||||
print("ok")
|
||||
# sacd = "nwvgaw6iiibv4fp"
|
||||
# book = {
|
||||
# "author": sacd,
|
||||
# "name": "A study in red",
|
||||
# "rating": 4.5,
|
||||
# "summary": "The worst Sherlock Homes book",
|
||||
# }
|
||||
# data = pb.records.create("books", book)
|
||||
# print(data)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Union
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
from pocketbase.utils import to_datetime
|
||||
@@ -8,9 +9,9 @@ from pocketbase.models.utils.base_model import BaseModel
|
||||
class Admin(BaseModel):
|
||||
avatar: int
|
||||
email: str
|
||||
last_reset_sent_at: Union[str, datetime.datetime]
|
||||
last_reset_sent_at: str | datetime.datetime
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
self.avatar = data.get("avatar", 0)
|
||||
self.email = data.get("email", "")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
from pocketbase.models.utils.schema_field import SchemaField
|
||||
@@ -8,13 +8,13 @@ class Collection(BaseModel):
|
||||
name: str
|
||||
schema: list[SchemaField]
|
||||
system: bool
|
||||
list_rule: Optional[str]
|
||||
view_rule: Optional[str]
|
||||
create_rule: Optional[str]
|
||||
update_rule: Optional[str]
|
||||
delete_rule: Optional[str]
|
||||
list_rule: str | None
|
||||
view_rule: str | None
|
||||
create_rule: str | None
|
||||
update_rule: str | None
|
||||
delete_rule: str | None
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
self.name = data.get("name", "")
|
||||
self.system = data.get("system", False)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
|
||||
|
||||
@@ -7,7 +8,7 @@ class ExternalAuth(BaseModel):
|
||||
provider: str
|
||||
provider_id: str
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
self.user_id = data.get("userId", "")
|
||||
self.provider = data.get("provider", "")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
|
||||
@@ -12,9 +12,9 @@ class LogRequest(BaseModel):
|
||||
user_ip: str
|
||||
referer: str
|
||||
user_agent: str
|
||||
meta: dict[str:Any]
|
||||
meta: dict
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
self.url = data.get("url", "")
|
||||
self.method = data.get("method", "")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
from pocketbase.utils import camel_to_snake
|
||||
@@ -7,9 +7,9 @@ from pocketbase.utils import camel_to_snake
|
||||
class Record(BaseModel):
|
||||
collection_id: str
|
||||
collection_name: str
|
||||
expand: dict[str:Any]
|
||||
expand: dict
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
for key, value in data.items():
|
||||
key = camel_to_snake(key).replace("@", "")
|
||||
@@ -22,7 +22,7 @@ class Record(BaseModel):
|
||||
self.load_expanded()
|
||||
|
||||
@classmethod
|
||||
def parse_expanded(cls, data: dict[str:Any]):
|
||||
def parse_expanded(cls, data: dict):
|
||||
return cls(data)
|
||||
|
||||
def load_expanded(self) -> None:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from typing import Any, Optional, Union
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Union
|
||||
import datetime
|
||||
|
||||
from pocketbase.utils import to_datetime
|
||||
@@ -9,11 +11,11 @@ from pocketbase.models.utils.base_model import BaseModel
|
||||
class User(BaseModel):
|
||||
email: str
|
||||
verified: bool
|
||||
last_reset_sent_at: Union[str, datetime.datetime]
|
||||
last_verification_sent_at: Union[str, datetime.datetime]
|
||||
profile: Optional[Record]
|
||||
last_reset_sent_at: str | datetime.datetime
|
||||
last_verification_sent_at: str | datetime.datetime
|
||||
profile: Record | None
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def load(self, data: dict) -> None:
|
||||
super().load(data)
|
||||
self.email = data.get("email", "")
|
||||
self.verified = data.get("verified", "")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from typing import Any, Union
|
||||
import datetime
|
||||
|
||||
from pocketbase.utils import to_datetime
|
||||
@@ -7,14 +8,20 @@ from pocketbase.utils import to_datetime
|
||||
|
||||
class BaseModel(ABC):
|
||||
id: str
|
||||
created: Union[str, datetime.datetime]
|
||||
updated: Union[str, datetime.datetime]
|
||||
created: str | datetime.datetime
|
||||
updated: str | datetime.datetime
|
||||
|
||||
def __init__(self, data: dict[str:Any] = {}) -> None:
|
||||
def __init__(self, data: dict = {}) -> None:
|
||||
super().__init__()
|
||||
self.load(data)
|
||||
|
||||
def load(self, data: dict[str:Any]) -> None:
|
||||
def __str__(self) -> str:
|
||||
return f"<{self.__class__.__name__}: {self.id}>"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
def load(self, data: dict) -> None:
|
||||
"""Loads `data` into the current model."""
|
||||
self.id = data.pop("id", "")
|
||||
self.created = to_datetime(data.pop("created", ""))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -10,4 +11,4 @@ class SchemaField:
|
||||
system: bool = False
|
||||
required: bool = False
|
||||
unique: bool = False
|
||||
options: dict[str:Any] = field(default_factory=dict)
|
||||
options: dict = field(default_factory=dict)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
from pocketbase.services.utils.crud_service import CrudService
|
||||
from pocketbase.models.admin import Admin
|
||||
@@ -16,7 +17,7 @@ class AdminAuthResponse:
|
||||
|
||||
|
||||
class Admins(CrudService):
|
||||
def decode(self, data: dict[str:Any]) -> BaseModel:
|
||||
def decode(self, data: dict) -> BaseModel:
|
||||
return Admin(data)
|
||||
|
||||
def base_crud_path(self) -> str:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.services.utils.crud_service import CrudService
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
@@ -6,7 +6,7 @@ from pocketbase.models.collection import Collection
|
||||
|
||||
|
||||
class Collections(CrudService):
|
||||
def decode(self, data: dict[str:Any]) -> BaseModel:
|
||||
def decode(self, data: dict) -> BaseModel:
|
||||
return Collection(data)
|
||||
|
||||
def base_crud_path(self) -> str:
|
||||
@@ -14,7 +14,7 @@ class Collections(CrudService):
|
||||
|
||||
def import_collections(
|
||||
self,
|
||||
collections: list[Collection],
|
||||
collections: list,
|
||||
delete_missing: bool = False,
|
||||
query_params: dict = {},
|
||||
) -> bool:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Union
|
||||
from urllib.parse import quote
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
from typing import Callable, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
import dataclasses
|
||||
import json
|
||||
|
||||
from pocketbase.services.utils.base_service import BaseService
|
||||
from pocketbase.services.utils.sse import Event, SSEClient
|
||||
from pocketbase.models.record import Record
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class MessageData:
|
||||
action: str
|
||||
record: Record
|
||||
|
||||
|
||||
class Realtime(BaseService):
|
||||
client_id: str
|
||||
subscriptions: dict
|
||||
client_id: str = ""
|
||||
event_source: SSEClient | None = None
|
||||
|
||||
def subscribe(self, subscription: str, callback: Callable) -> None:
|
||||
def __init__(self, client) -> None:
|
||||
super().__init__(client)
|
||||
self.subscriptions = {}
|
||||
self.client_id = ""
|
||||
self.event_source = None
|
||||
|
||||
def subscribe(
|
||||
self, subscription: str, callback: Callable[[MessageData], None]
|
||||
) -> None:
|
||||
"""Inits the sse connection (if not already) and register the subscription."""
|
||||
self.subscriptions[subscription] = callback
|
||||
# unsubscribe existing
|
||||
if subscription in self.subscriptions and self.event_source:
|
||||
self.event_source.remove_event_listener(subscription, callback)
|
||||
# register subscription
|
||||
self.subscriptions[subscription] = self._make_subscription(callback)
|
||||
if not self.event_source:
|
||||
self._connect()
|
||||
elif self.client_id:
|
||||
self._submit_subscriptions()
|
||||
|
||||
def unsubscribe(self, subscription: Optional[str] = None) -> None:
|
||||
def unsubscribe(self, subscription: str | None = None) -> None:
|
||||
"""
|
||||
Unsubscribe from a subscription.
|
||||
|
||||
@@ -21,29 +50,79 @@ class Realtime(BaseService):
|
||||
The related sse connection will be autoclosed if after the
|
||||
unsubscribe operations there are no active subscriptions left.
|
||||
"""
|
||||
pass
|
||||
if not subscription:
|
||||
self._remove_subscription_listeners()
|
||||
self.subscriptions = {}
|
||||
elif subscription in self.subscriptions:
|
||||
self.event_source.remove_event_listener(
|
||||
subscription, self.subscriptions[subscription]
|
||||
)
|
||||
self.subscriptions.pop(subscription)
|
||||
else:
|
||||
return
|
||||
if self.client_id:
|
||||
self._submit_subscriptions()
|
||||
if not self.subscriptions:
|
||||
self._disconnect()
|
||||
|
||||
def _make_subscription(
|
||||
self, callback: Callable[[MessageData], None]
|
||||
) -> Callable[[Event], None]:
|
||||
def listener(event: Event) -> None:
|
||||
data = json.loads(event.data)
|
||||
if "record" in data and "action" in data:
|
||||
callback(
|
||||
MessageData(
|
||||
action=data["action"],
|
||||
record=Record(
|
||||
data=data["record"],
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return listener
|
||||
|
||||
def _submit_subscriptions(self) -> bool:
|
||||
self._add_subscription_listeners()
|
||||
self.client.send(
|
||||
"/api/realtime",
|
||||
{
|
||||
"method": "POST",
|
||||
"body": {
|
||||
"clientId": self.client_id,
|
||||
"subscriptions": self.subscriptions.keys(),
|
||||
"subscriptions": list(self.subscriptions.keys()),
|
||||
},
|
||||
},
|
||||
)
|
||||
return True
|
||||
|
||||
def _add_subscription_listeners(self) -> None:
|
||||
pass
|
||||
if not self.event_source:
|
||||
return
|
||||
self._remove_subscription_listeners()
|
||||
for subscription, callback in self.subscriptions.items():
|
||||
self.event_source.add_event_listener(subscription, callback)
|
||||
|
||||
def _remove_subscription_listeners(self) -> None:
|
||||
pass
|
||||
if not self.event_source:
|
||||
return
|
||||
for subscription, callback in self.subscriptions.items():
|
||||
self.event_source.remove_event_listener(subscription, callback)
|
||||
|
||||
def _connect_handler(self, event: Event) -> None:
|
||||
self.client_id = event.id
|
||||
self._submit_subscriptions()
|
||||
|
||||
def _connect(self) -> None:
|
||||
pass
|
||||
self._disconnect()
|
||||
self.event_source = SSEClient(self.client.build_url("/api/realtime"))
|
||||
self.event_source.add_event_listener("PB_CONNECT", self._connect_handler)
|
||||
|
||||
def _disconnect(self) -> None:
|
||||
pass
|
||||
self._remove_subscription_listeners()
|
||||
self.client_id = ""
|
||||
if not self.event_source:
|
||||
return
|
||||
self.event_source.remove_event_listener("PB_CONNECT", self._connect_handler)
|
||||
self.event_source.close()
|
||||
self.event_source = None
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any
|
||||
from __future__ import annotations
|
||||
|
||||
from urllib.parse import quote, urlencode
|
||||
|
||||
from pocketbase.services.utils.sub_crud_service import SubCrudService
|
||||
@@ -7,7 +8,7 @@ from pocketbase.models.record import Record
|
||||
|
||||
|
||||
class Records(SubCrudService):
|
||||
def decode(self, data: dict[str:Any]) -> BaseModel:
|
||||
def decode(self, data: dict) -> BaseModel:
|
||||
return Record(data)
|
||||
|
||||
def base_crud_path(self, collection_id_or_name: str) -> str:
|
||||
@@ -22,5 +23,5 @@ class Records(SubCrudService):
|
||||
base_url = base_url[:-1]
|
||||
result = f"{base_url}/api/files/{record.collection_id}/{record.id}/{filename}"
|
||||
if query_params:
|
||||
results += "?" + urlencode(query_params)
|
||||
result += "?" + urlencode(query_params)
|
||||
return result
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pocketbase.services.utils.base_service import BaseService
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
from pocketbase.services.utils.crud_service import CrudService
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
from pocketbase.models.user import User
|
||||
@@ -35,7 +38,7 @@ class AuthMethodsList:
|
||||
|
||||
|
||||
class Users(CrudService):
|
||||
def decode(self, data: dict[str:Any]) -> BaseModel:
|
||||
def decode(self, data: dict) -> BaseModel:
|
||||
return User(data)
|
||||
|
||||
def base_crud_path(self) -> str:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from urllib.parse import quote
|
||||
from typing import Any
|
||||
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
from pocketbase.models.utils.list_result import ListResult
|
||||
@@ -9,7 +9,7 @@ from pocketbase.services.utils.base_service import BaseService
|
||||
|
||||
|
||||
class BaseCrudService(BaseService, ABC):
|
||||
def decode(self, data: dict[str:Any]) -> BaseModel:
|
||||
def decode(self, data: dict) -> BaseModel:
|
||||
"""Response data decoder"""
|
||||
|
||||
def _get_full_list(
|
||||
@@ -18,7 +18,7 @@ class BaseCrudService(BaseService, ABC):
|
||||
|
||||
result: list[BaseModel] = []
|
||||
|
||||
def request(result: list[BaseModel], page: int) -> list[Any]:
|
||||
def request(result: list[BaseModel], page: int) -> list:
|
||||
list = self._get_list(base_path, page, batch_size, query_params)
|
||||
items = list.items
|
||||
total_items = list.total_items
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
|
||||
139
pocketbase/services/utils/sse.py
Normal file
139
pocketbase/services/utils/sse.py
Normal file
@@ -0,0 +1,139 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
import dataclasses
|
||||
import threading
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Event:
|
||||
"""Representation of an event"""
|
||||
|
||||
id: str = ""
|
||||
event: str = "message"
|
||||
data: str = ""
|
||||
retry: int | None = None
|
||||
|
||||
|
||||
class EventLoop(threading.Thread):
|
||||
FIELD_SEPARATOR = ":"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
url: str,
|
||||
method: str = "GET",
|
||||
headers: dict | None = None,
|
||||
payload: dict | None = None,
|
||||
encoding="utf-8",
|
||||
listeners: dict[str, Callable] | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
threading.Thread.__init__(self, **kwargs)
|
||||
self.kill = False
|
||||
self.client = httpx.Client()
|
||||
self.url = url
|
||||
self.method = method
|
||||
self.headers = headers
|
||||
self.payload = payload
|
||||
self.encoding = encoding
|
||||
self.listeners = listeners or {}
|
||||
|
||||
def _read(self):
|
||||
"""Read the incoming event source stream and yield event chunks"""
|
||||
data = b""
|
||||
with self.client.stream(
|
||||
self.method,
|
||||
self.url,
|
||||
headers=self.headers,
|
||||
data=self.payload,
|
||||
timeout=None,
|
||||
) as r:
|
||||
for chunk in r.iter_bytes():
|
||||
for line in chunk.splitlines(True):
|
||||
data += line
|
||||
if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")):
|
||||
yield data
|
||||
data = b""
|
||||
if data:
|
||||
yield data
|
||||
|
||||
def _events(self):
|
||||
for chunk in self._read():
|
||||
event = Event()
|
||||
for line in chunk.splitlines():
|
||||
line = line.decode(self.encoding)
|
||||
if not line.strip() or line.startswith(self.FIELD_SEPARATOR):
|
||||
continue
|
||||
data = line.split(self.FIELD_SEPARATOR, 1)
|
||||
field = data[0]
|
||||
if field not in event.__dict__:
|
||||
continue
|
||||
if len(data) > 1:
|
||||
if data[1].startswith(" "):
|
||||
value = data[1][1:]
|
||||
else:
|
||||
value = data[1]
|
||||
else:
|
||||
value = ""
|
||||
if field == "data":
|
||||
event.data += value + "\n"
|
||||
else:
|
||||
setattr(event, field, value)
|
||||
if not event.data:
|
||||
continue
|
||||
if event.data.endswith("\n"):
|
||||
event.data = event.data[0:-1]
|
||||
event.event = event.event or "message"
|
||||
yield event
|
||||
|
||||
def run(self):
|
||||
for event in self._events():
|
||||
if self.kill:
|
||||
break
|
||||
if event.event in self.listeners:
|
||||
self.listeners[event.event](event)
|
||||
|
||||
|
||||
class SSEClient:
|
||||
"""Implementation of a server side event client"""
|
||||
|
||||
_listeners: dict = {}
|
||||
_loop_thread: threading.Thread | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
url: str,
|
||||
method: str = "GET",
|
||||
headers: dict | None = None,
|
||||
payload: dict | None = None,
|
||||
encoding="utf-8",
|
||||
) -> None:
|
||||
self._listeners = {}
|
||||
self._loop_thread = EventLoop(
|
||||
url=url,
|
||||
method=method,
|
||||
headers=headers,
|
||||
payload=payload,
|
||||
encoding=encoding,
|
||||
listeners=self._listeners,
|
||||
name="loop",
|
||||
)
|
||||
self._loop_thread.daemon = True
|
||||
self._loop_thread.start()
|
||||
|
||||
def add_event_listener(self, event: str, callback: Callable[[Event], None]) -> None:
|
||||
self._listeners[event] = callback
|
||||
self._loop_thread.listeners = self._listeners
|
||||
|
||||
def remove_event_listener(
|
||||
self, event: str, callback: Callable[[Event], None]
|
||||
) -> None:
|
||||
if event in self._listeners:
|
||||
self._listeners.pop(event)
|
||||
self._loop_thread.listeners = self._listeners
|
||||
|
||||
def close(self) -> None:
|
||||
# TODO: does not work like this
|
||||
self._loop_thread.kill = True
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
|
||||
from pocketbase.models.utils.base_model import BaseModel
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC
|
||||
from typing import Union, Optional
|
||||
|
||||
from pocketbase.models.admin import Admin
|
||||
from pocketbase.models.user import User
|
||||
@@ -12,26 +13,26 @@ class BaseAuthStore(ABC):
|
||||
"""
|
||||
|
||||
base_token: str
|
||||
base_model: Union[User, Admin, None]
|
||||
base_model: User | Admin | None
|
||||
|
||||
def __init__(
|
||||
self, base_token: str = "", base_model: Optional[Union[User, Admin]] = None
|
||||
self, base_token: str = "", base_model: User | Admin | None = None
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.base_token = base_token
|
||||
self.base_model = base_model
|
||||
|
||||
@property
|
||||
def token(self) -> Union[str, None]:
|
||||
def token(self) -> str | None:
|
||||
"""Retrieves the stored token (if any)."""
|
||||
return self.base_token
|
||||
|
||||
@property
|
||||
def model(self) -> Union[User, Admin, None]:
|
||||
def model(self) -> User | Admin | None:
|
||||
"""Retrieves the stored model data (if any)."""
|
||||
return self.base_model
|
||||
|
||||
def save(self, token: str = "", model: Optional[Union[User, Admin]] = None) -> None:
|
||||
def save(self, token: str = "", model: User | Admin | None = None) -> None:
|
||||
"""Saves the provided new token and model data in the auth store."""
|
||||
self.base_token = token
|
||||
self.base_model = model
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from typing import Any, Optional, Union
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
import pickle
|
||||
import os
|
||||
|
||||
@@ -16,7 +18,7 @@ class LocalAuthStore(BaseAuthStore):
|
||||
filename: str = "pocketbase_auth.data",
|
||||
filepath: str = "",
|
||||
base_token: str = "",
|
||||
base_model: Optional[Union[User, Admin]] = None,
|
||||
base_model: User | Admin | None = None,
|
||||
) -> None:
|
||||
super().__init__(base_token, base_model)
|
||||
self.filename = filename
|
||||
@@ -26,18 +28,18 @@ class LocalAuthStore(BaseAuthStore):
|
||||
@property
|
||||
def token(self) -> str:
|
||||
data = self._storage_get(self.complete_filepath)
|
||||
if not data or not "token" in data:
|
||||
if not data or "token" not in data:
|
||||
return None
|
||||
return data["token"]
|
||||
|
||||
@property
|
||||
def model(self) -> Union[User, Admin, None]:
|
||||
def model(self) -> User | Admin | None:
|
||||
data = self._storage_get(self.complete_filepath)
|
||||
if not data or not "model" in data:
|
||||
if not data or "model" not in data:
|
||||
return None
|
||||
return data["model"]
|
||||
|
||||
def save(self, token: str = "", model: Optional[Union[User, Admin]] = None) -> None:
|
||||
def save(self, token: str = "", model: User | Admin | None = None) -> None:
|
||||
self._storage_set(self.complete_filepath, {"token": token, "model": model})
|
||||
super().save(token, model)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import datetime
|
||||
from typing import Union
|
||||
|
||||
|
||||
def camel_to_snake(name: str) -> str:
|
||||
@@ -10,7 +11,7 @@ def camel_to_snake(name: str) -> str:
|
||||
|
||||
def to_datetime(
|
||||
str_datetime: str, format: str = "%Y-%m-%d %H:%M:%S"
|
||||
) -> Union[datetime.datetime, str]:
|
||||
) -> datetime.datetime | str:
|
||||
str_datetime = str_datetime.split(".")[0]
|
||||
try:
|
||||
return datetime.datetime.strptime(str_datetime, format)
|
||||
|
||||
297
poetry.lock
generated
297
poetry.lock
generated
@@ -9,20 +9,13 @@ python-versions = ">=3.6.2"
|
||||
[package.dependencies]
|
||||
idna = ">=2.8"
|
||||
sniffio = ">=1.1"
|
||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
|
||||
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
|
||||
trio = ["trio (>=0.16)"]
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.1"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "22.1.0"
|
||||
@@ -51,6 +44,8 @@ mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
||||
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
@@ -60,7 +55,7 @@ uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.6.15.2"
|
||||
version = "2022.9.24"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -76,6 +71,7 @@ python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
@@ -85,6 +81,34 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "6.4.4"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
|
||||
|
||||
[package.extras]
|
||||
toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "5.0.4"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""}
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.9.0,<2.10.0"
|
||||
pyflakes = ">=2.5.0,<2.6.0"
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.12.0"
|
||||
@@ -140,12 +164,36 @@ optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "8.14.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
name = "importlib-metadata"
|
||||
version = "4.2.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"]
|
||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mccabe"
|
||||
version = "0.7.0"
|
||||
description = "McCabe checker, plugin for flake8"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
@@ -188,14 +236,18 @@ test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "0.13.1"
|
||||
version = "1.0.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
@@ -205,6 +257,22 @@ category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.9.1"
|
||||
description = "Python style guide checker"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "2.5.0"
|
||||
description = "passive checker of Python programs"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
@@ -218,25 +286,39 @@ diagrams = ["jinja2", "railroad-diagrams"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "5.4.3"
|
||||
version = "7.1.3"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=17.4.0"
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
more-itertools = ">=4.0.0"
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0"
|
||||
py = ">=1.5.0"
|
||||
wcwidth = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
py = ">=1.8.2"
|
||||
tomli = ">=1.0.0"
|
||||
|
||||
[package.extras]
|
||||
checkqa-mypy = ["mypy (==v0.761)"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "3.0.0"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
coverage = {version = ">=5.2.1", extras = ["toml"]}
|
||||
pytest = ">=4.6"
|
||||
|
||||
[package.extras]
|
||||
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
|
||||
|
||||
[[package]]
|
||||
name = "rfc3986"
|
||||
@@ -269,26 +351,43 @@ optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.5"
|
||||
description = "Measures the displayed width of unicode strings in a terminal"
|
||||
name = "typed-ast"
|
||||
version = "1.5.4"
|
||||
description = "a fork of Python 2 and 3 ast modules with type comment support"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.3.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.8.1"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
|
||||
testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "8e1b477e300709c958c814a3937ee98aa175988857e7452bc5e3e2b97f58f7ca"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "78af276171c1e41e0cb3205383b288b9ed15cb8fdf68e7184954909e8b07c7c6"
|
||||
|
||||
[metadata.files]
|
||||
anyio = [
|
||||
{file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"},
|
||||
{file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
|
||||
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
|
||||
@@ -319,8 +418,8 @@ black = [
|
||||
{file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2022.6.15.2-py3-none-any.whl", hash = "sha256:0aa1a42fbd57645fabeb6290a7687c21755b0344ecaeaa05f4e9f6207ae2e9a8"},
|
||||
{file = "certifi-2022.6.15.2.tar.gz", hash = "sha256:aa08c101214127b9b0472ca6338315113c9487d45376fd3e669201b477c71003"},
|
||||
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
|
||||
{file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
@@ -330,6 +429,62 @@ colorama = [
|
||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"},
|
||||
{file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"},
|
||||
{file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"},
|
||||
{file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"},
|
||||
{file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"},
|
||||
{file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"},
|
||||
{file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
|
||||
{file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
|
||||
]
|
||||
flake8 = [
|
||||
{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
|
||||
{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
|
||||
]
|
||||
h11 = [
|
||||
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
|
||||
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
|
||||
@@ -346,9 +501,17 @@ idna = [
|
||||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
more-itertools = [
|
||||
{file = "more-itertools-8.14.0.tar.gz", hash = "sha256:c09443cd3d5438b8dafccd867a6bc1cb0894389e90cb53d227456b0b0bccb750"},
|
||||
{file = "more_itertools-8.14.0-py3-none-any.whl", hash = "sha256:1bc4f91ee5b1b31ac7ceacc17c09befe6a40a503907baf9c839c229b5095cfd2"},
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"},
|
||||
{file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
mccabe = [
|
||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
@@ -367,20 +530,32 @@ platformdirs = [
|
||||
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
||||
]
|
||||
pycodestyle = [
|
||||
{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
|
||||
{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
|
||||
]
|
||||
pyflakes = [
|
||||
{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
|
||||
{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
|
||||
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
|
||||
{file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
|
||||
{file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
|
||||
]
|
||||
pytest-cov = [
|
||||
{file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
|
||||
{file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
|
||||
]
|
||||
rfc3986 = [
|
||||
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
|
||||
@@ -394,7 +569,37 @@ tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
wcwidth = [
|
||||
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"},
|
||||
{file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"},
|
||||
{file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"},
|
||||
{file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"},
|
||||
{file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"},
|
||||
{file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"},
|
||||
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
|
||||
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"},
|
||||
{file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"},
|
||||
]
|
||||
|
||||
@@ -1,15 +1,51 @@
|
||||
[project]
|
||||
name = "pocketbase"
|
||||
description = "PocketBase SDK for python."
|
||||
requires-python = ">=3.7"
|
||||
license = "MIT"
|
||||
authors = [
|
||||
{ name = "Vithor Jaeger", email = "vaphes@gmail.com" },
|
||||
]
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
"Intended Audience :: Developers",
|
||||
]
|
||||
keywords = "pocketbase sdk"
|
||||
dependencies = ["httpx>=0.23.0"]
|
||||
dynamic = ["readme", "version"]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/vaphes/pocketbase"
|
||||
"Source" = "https://github.com/vaphes/pocketbase"
|
||||
"Bug Tracker" = "https://github.com/vaphes/pocketbase/issues"
|
||||
|
||||
[tool.poetry]
|
||||
name = "pocketbase"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
version = "0.2.2"
|
||||
description = "PocketBase SDK for python."
|
||||
authors = ["Vithor Jaeger <vaphes@gmail.com>"]
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/vaphes/pocketbase"
|
||||
repository = "https://github.com/vaphes/pocketbase"
|
||||
keywords = ["pocketbase", "sdk"]
|
||||
|
||||
[tool.poetry.urls]
|
||||
"Bug Tracker" = "https://github.com/vaphes/pocketbase/issues"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
python = "^3.7"
|
||||
httpx = "^0.23.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^5.2"
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
flake8 = "^5.0.4"
|
||||
pytest = "^7.1.3"
|
||||
black = {version = "^22.8.0", allow-prereleases = true}
|
||||
|
||||
[build-system]
|
||||
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
httpx >= 0.23.0
|
||||
@@ -1,5 +0,0 @@
|
||||
from pocketbase import __version__
|
||||
|
||||
|
||||
def test_version():
|
||||
assert __version__ == "0.1.0"
|
||||
18
tests/test_utils.py
Normal file
18
tests/test_utils.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import datetime
|
||||
|
||||
from pocketbase import __version__
|
||||
from pocketbase.utils import camel_to_snake, to_datetime
|
||||
|
||||
|
||||
def test_version():
|
||||
assert __version__ == "0.2.2"
|
||||
|
||||
|
||||
def test_utils():
|
||||
assert camel_to_snake("TestCase") == "test_case"
|
||||
assert camel_to_snake("test_case") == "test_case"
|
||||
assert camel_to_snake("TestBS123") == "test_bs123"
|
||||
assert to_datetime("2022-01-31 12:01:05") == datetime.datetime(
|
||||
2022, 1, 31, 12, 1, 5
|
||||
)
|
||||
assert isinstance(to_datetime("2022-01-31"), str)
|
||||
Reference in New Issue
Block a user