mirror of
https://github.com/kennethreitz/replit-py.git
synced 2026-06-05 23:10:18 +00:00
use new key query param and add some tests (#13)
* first test * unescape returned keys * fix to_dict * wip * a bit of sync * dict methods * lint * oop * run the tests * concurrently * secret
This commit is contained in:
@@ -5,8 +5,10 @@ agent:
|
||||
type: e1-standard-2
|
||||
os_image: ubuntu1804
|
||||
blocks:
|
||||
- name: Lint
|
||||
- name: Lint and test
|
||||
task:
|
||||
secrets:
|
||||
- name: Repl.it Database
|
||||
jobs:
|
||||
- name: flake8
|
||||
commands:
|
||||
@@ -15,3 +17,11 @@ blocks:
|
||||
- python -m pip install --upgrade poetry
|
||||
- poetry install
|
||||
- git diff origin/master | poetry run flake8 --diff
|
||||
- name: unittest
|
||||
commands:
|
||||
- sem-version python 3.8
|
||||
- checkout --use-cache
|
||||
- python -m pip install --upgrade poetry
|
||||
- poetry install
|
||||
- poetry run coverage run -m unittest src/replit/test_database.py
|
||||
- poetry run coverage report -m
|
||||
|
||||
Generated
+54
-3
@@ -76,9 +76,9 @@ version = "1.6.2"
|
||||
[package.dependencies]
|
||||
GitPython = ">=1.0.1"
|
||||
PyYAML = ">=3.13"
|
||||
colorama = ">=0.3.9"
|
||||
six = ">=1.10.0"
|
||||
stevedore = ">=1.20.0"
|
||||
colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
@@ -127,11 +127,23 @@ version = "7.1.2"
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Cross-platform colored terminal text."
|
||||
marker = "platform_system == \"Windows\" or sys_platform == \"win32\""
|
||||
name = "colorama"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "0.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Code coverage measurement for Python"
|
||||
name = "coverage"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
version = "5.2.1"
|
||||
|
||||
[package.extras]
|
||||
toml = ["toml"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
|
||||
@@ -232,6 +244,7 @@ version = "0.18.1"
|
||||
|
||||
[package.dependencies]
|
||||
pycodestyle = "*"
|
||||
setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
@@ -505,10 +518,12 @@ Jinja2 = ">=2.3"
|
||||
Pygments = ">=2.0"
|
||||
alabaster = ">=0.7,<0.8"
|
||||
babel = ">=1.3"
|
||||
colorama = ">=0.3.5"
|
||||
docutils = ">=0.12"
|
||||
imagesize = "*"
|
||||
packaging = "*"
|
||||
requests = ">=2.5.0"
|
||||
setuptools = "*"
|
||||
snowballstemmer = ">=1.1"
|
||||
sphinxcontrib-applehelp = "*"
|
||||
sphinxcontrib-devhelp = "*"
|
||||
@@ -516,7 +531,6 @@ sphinxcontrib-htmlhelp = "*"
|
||||
sphinxcontrib-jsmath = "*"
|
||||
sphinxcontrib-qthelp = "*"
|
||||
sphinxcontrib-serializinghtml = "*"
|
||||
colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinxcontrib-websupport"]
|
||||
@@ -696,7 +710,8 @@ idna = ">=2.0"
|
||||
multidict = ">=4.0"
|
||||
|
||||
[metadata]
|
||||
content-hash = "c92c18a07248bbd0df18fba9327c2387af07b06a9a489123e3276810fb4e89fc"
|
||||
content-hash = "9ceb6e910237c20a8cf4b40b1c12620752f1e9fe3f0417cba404e4ce2ef10752"
|
||||
lock-version = "1.0"
|
||||
python-versions = "^3.8"
|
||||
|
||||
[metadata.files]
|
||||
@@ -758,6 +773,42 @@ colorama = [
|
||||
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
||||
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"},
|
||||
{file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"},
|
||||
{file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"},
|
||||
{file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"},
|
||||
{file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"},
|
||||
{file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"},
|
||||
{file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"},
|
||||
{file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"},
|
||||
{file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"},
|
||||
{file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"},
|
||||
{file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"},
|
||||
{file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"},
|
||||
{file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"},
|
||||
{file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"},
|
||||
{file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"},
|
||||
{file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"},
|
||||
{file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"},
|
||||
{file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"},
|
||||
{file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"},
|
||||
{file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"},
|
||||
{file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"},
|
||||
{file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"},
|
||||
{file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"},
|
||||
{file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"},
|
||||
{file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"},
|
||||
{file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"},
|
||||
{file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"},
|
||||
{file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"},
|
||||
{file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"},
|
||||
{file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"},
|
||||
{file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"},
|
||||
{file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"},
|
||||
{file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"},
|
||||
{file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"},
|
||||
]
|
||||
darglint = [
|
||||
{file = "darglint-1.5.2-py3-none-any.whl", hash = "sha256:049a98cf3aec8cf6ea344a863c68112d80b7f8de214459b5fa6853371f89c3e7"},
|
||||
{file = "darglint-1.5.2.tar.gz", hash = "sha256:6b9461f96694c2cf1d8edb1597a783fe6840953b0eb18cc6cc1e72a26f196d79"},
|
||||
|
||||
@@ -16,6 +16,7 @@ flask = "^1.1.2"
|
||||
werkzeug = "^1.0.1"
|
||||
aiohttp = "^3.6.2"
|
||||
nest_asyncio = "^1.4.0"
|
||||
coverage = "^5.2.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
flake8 = "^3.8.3"
|
||||
|
||||
@@ -5,6 +5,7 @@ import json
|
||||
import os
|
||||
from sys import stderr
|
||||
from typing import Any, Callable, Dict, Tuple, Union
|
||||
import urllib
|
||||
|
||||
import aiohttp
|
||||
import nest_asyncio
|
||||
@@ -20,7 +21,7 @@ class AsyncJSONKey:
|
||||
you don't have to do it manually.
|
||||
"""
|
||||
|
||||
__slots__ = ("db", "key", "dtype", "get_default", "discard_bad_data")
|
||||
__slots__ = ("db", "key", "dtype", "get_default", "discard_bad_data", "do_raise")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -216,14 +217,15 @@ class AsyncReplitDb:
|
||||
Returns:
|
||||
Tuple[str]: The keys found.
|
||||
"""
|
||||
params = {"prefix": prefix, "encode": "true"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(self.db_url + "?prefix=" + prefix) as response:
|
||||
async with session.get(self.db_url, params=params) as response:
|
||||
response.raise_for_status()
|
||||
text = await response.text()
|
||||
if not text:
|
||||
return tuple()
|
||||
else:
|
||||
return tuple(text.split("\n"))
|
||||
return tuple(urllib.parse.unquote(k) for k in text.split("\n"))
|
||||
|
||||
async def to_dict(self, prefix: str = "") -> Dict[str, str]:
|
||||
"""Dump all data in the database into a dictionary.
|
||||
@@ -238,7 +240,7 @@ class AsyncReplitDb:
|
||||
ret = {}
|
||||
keys = await self.list(prefix=prefix)
|
||||
for i in keys:
|
||||
ret[i] = await self.view(i)
|
||||
ret[i] = await self.get(i)
|
||||
return ret
|
||||
|
||||
async def keys(self) -> Tuple[str]:
|
||||
@@ -263,6 +265,7 @@ class AsyncReplitDb:
|
||||
dtype: JSON_TYPE,
|
||||
get_default: Callable = None,
|
||||
discard_bad_data: bool = False,
|
||||
do_raise: bool = False,
|
||||
) -> AsyncJSONKey:
|
||||
"""Initialize an AsyncJSONKey instance.
|
||||
|
||||
@@ -277,6 +280,7 @@ class AsyncReplitDb:
|
||||
argument is used.
|
||||
discard_bad_data (bool): Don't prompt if bad data is read, overwrite it
|
||||
with the default. Defaults to False.
|
||||
do_raise (bool): Whether to raise exceptions when errors are encountered.
|
||||
|
||||
Returns:
|
||||
AsyncJSONKey: The initialized AsyncJSONKey instance.
|
||||
@@ -287,6 +291,7 @@ class AsyncReplitDb:
|
||||
dtype=dtype,
|
||||
get_default=get_default,
|
||||
discard_bad_data=discard_bad_data,
|
||||
do_raise=do_raise,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
"""Tests for replit.database."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from replit.database import AsyncReplitDb, ReplitDb
|
||||
import requests
|
||||
|
||||
|
||||
class TestAsyncDatabase(unittest.IsolatedAsyncioTestCase):
|
||||
"""Tests for replit.database.AsyncReplitDb."""
|
||||
|
||||
async def asyncSetUp(self) -> None:
|
||||
"""Grab a JWT for all the tests to share."""
|
||||
password = os.environ["PASSWORD"]
|
||||
req = requests.get(
|
||||
"https://database-test-jwt.kochman.repl.co", auth=("test", password)
|
||||
)
|
||||
url = req.text
|
||||
self.db = AsyncReplitDb(url)
|
||||
|
||||
async def asyncTearDown(self) -> None:
|
||||
"""Nuke whatever the test added."""
|
||||
for k in await self.db.keys():
|
||||
await self.db.delete(k)
|
||||
|
||||
async def test_get_set_delete(self) -> None:
|
||||
"""Test that we can get, set, and delete a key."""
|
||||
await self.db.set("test-key", "value")
|
||||
|
||||
val = await self.db.get("test-key")
|
||||
self.assertEqual(val, "value")
|
||||
|
||||
await self.db.delete("test-key")
|
||||
with self.assertRaises(KeyError):
|
||||
await self.db.get("test-key")
|
||||
|
||||
async def test_list_keys(self) -> None:
|
||||
"""Test that we can list keys."""
|
||||
key = "test-list-keys-with\nnewline"
|
||||
await self.db.set(key, "value")
|
||||
|
||||
val = await self.db.get(key)
|
||||
self.assertEqual(val, "value")
|
||||
|
||||
keys = await self.db.list(key)
|
||||
self.assertEqual(keys, (key,))
|
||||
|
||||
await self.db.delete(key)
|
||||
with self.assertRaises(KeyError):
|
||||
await self.db.get(key)
|
||||
|
||||
async def test_list_values(self) -> None:
|
||||
"""Test that we can get all values."""
|
||||
key = "test-list-values"
|
||||
await self.db.set(key + "1", "value1")
|
||||
await self.db.set(key + "2", "value2")
|
||||
|
||||
vals = await self.db.values()
|
||||
self.assertTupleEqual(vals, ("value1", "value2"))
|
||||
|
||||
async def test_dict(self) -> None:
|
||||
"""Test that we can get a dict."""
|
||||
await self.db.set("key1", "value")
|
||||
await self.db.set("key2", "value")
|
||||
d = await self.db.to_dict()
|
||||
self.assertDictEqual(d, {"key1": "value", "key2": "value"})
|
||||
|
||||
async def test_jsonkey(self) -> None:
|
||||
"""Test replit.database.AsyncJSONKey."""
|
||||
key = "test-jsonkey"
|
||||
|
||||
jk = self.db.jsonkey(key, dtype=str, do_raise=True)
|
||||
with self.assertRaises(KeyError):
|
||||
await jk.get()
|
||||
await jk.set("value")
|
||||
val = await jk.get()
|
||||
self.assertEqual(val, "value")
|
||||
|
||||
async def test_jsonkey_default(self) -> None:
|
||||
"""Test replit.database.AsyncJSONKey with a default callable."""
|
||||
key = "test-jsonkey"
|
||||
|
||||
jk = self.db.jsonkey(key, dtype=str, get_default=lambda: "value")
|
||||
val = await jk.get()
|
||||
self.assertEqual(val, "value")
|
||||
|
||||
|
||||
class TestDatabase(unittest.IsolatedAsyncioTestCase):
|
||||
"""Tests for replit.database.ReplitDb."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Grab a JWT for all the tests to share."""
|
||||
password = os.environ["PASSWORD"]
|
||||
req = requests.get(
|
||||
"https://database-test-jwt.kochman.repl.co", auth=("test", password)
|
||||
)
|
||||
url = req.text
|
||||
self.db = ReplitDb(url)
|
||||
|
||||
async def tearDown(self) -> None:
|
||||
"""Nuke whatever the test added."""
|
||||
for k in await self.db.keys():
|
||||
await self.db.delete(k)
|
||||
|
||||
def test_get_set_delete(self) -> None:
|
||||
"""Test get, set, and delete."""
|
||||
with self.assertRaises(KeyError):
|
||||
self.db.get("key")
|
||||
|
||||
self.db.set("key", "value")
|
||||
val = self.db.get("key")
|
||||
self.assertEqual(val, "value")
|
||||
|
||||
self.db.delete("key")
|
||||
|
||||
def test_dict(self) -> None:
|
||||
"""Test using the database as a dict."""
|
||||
with self.assertRaises(KeyError):
|
||||
val = self.db["hi"]
|
||||
|
||||
self.db["hi"] = "there"
|
||||
val = self.db.get("hi")
|
||||
self.assertEqual(val, "there")
|
||||
|
||||
del self.db["hi"]
|
||||
with self.assertRaises(KeyError):
|
||||
val = self.db["hi"]
|
||||
Reference in New Issue
Block a user