summaryrefslogtreecommitdiff
path: root/src/couch
diff options
context:
space:
mode:
authorVictor Näslund <victor@sunet.se>2022-11-02 15:31:23 +0100
committerVictor Näslund <victor@sunet.se>2022-11-02 15:31:23 +0100
commit8baecf339e8061160bee519e87ffe837d1525c18 (patch)
tree22664c10f22382b1d4647b5f2e96bcea4220d879 /src/couch
parentffb26f4a81a9ca61c4105df037f7e1beb8dc5fb0 (diff)
more freshup
Diffstat (limited to 'src/couch')
-rw-r--r--src/couch/__init__.py2
-rw-r--r--src/couch/client.py82
-rw-r--r--src/couch/feedreader.py8
-rw-r--r--src/couch/resource.py60
-rw-r--r--src/couch/utils.py6
5 files changed, 92 insertions, 66 deletions
diff --git a/src/couch/__init__.py b/src/couch/__init__.py
index a7537bc..64e0252 100644
--- a/src/couch/__init__.py
+++ b/src/couch/__init__.py
@@ -8,4 +8,4 @@ __email__ = "rinat.sabitov@gmail.com"
__status__ = "Development"
-from couch.client import Server # noqa: F401
+from .client import Server # noqa: F401
diff --git a/src/couch/client.py b/src/couch/client.py
index 52477be..96dc78a 100644
--- a/src/couch/client.py
+++ b/src/couch/client.py
@@ -8,10 +8,24 @@ import copy
import mimetypes
import warnings
-from couch import utils
-from couch import feedreader
-from couch import exceptions as exp
-from couch.resource import Resource
+from .utils import (
+ force_bytes,
+ force_text,
+ encode_view_options,
+ extract_credentials,
+)
+from .feedreader import (
+ SimpleFeedReader,
+ BaseFeedReader,
+)
+
+from .exceptions import (
+ Conflict,
+ NotFound,
+ FeedReaderExited,
+ UnexpectedError,
+)
+from .resource import Resource
DEFAULT_BASE_URL = os.environ.get('COUCHDB_URL', 'http://localhost:5984/')
@@ -25,16 +39,16 @@ def _id_to_path(_id: str) -> str:
def _listen_feed(object, node, feed_reader, **kwargs):
if not callable(feed_reader):
- raise exp.UnexpectedError("feed_reader must be callable or class")
+ raise UnexpectedError("feed_reader must be callable or class")
- if isinstance(feed_reader, feedreader.BaseFeedReader):
+ if isinstance(feed_reader, BaseFeedReader):
reader = feed_reader(object)
else:
- reader = feedreader.SimpleFeedReader()(object, feed_reader)
+ reader = SimpleFeedReader()(object, feed_reader)
# Possible options: "continuous", "longpoll"
kwargs.setdefault("feed", "continuous")
- data = utils.force_bytes(json.dumps(kwargs.pop('data', {})))
+ data = force_bytes(json.dumps(kwargs.pop('data', {})))
(resp, result) = object.resource(node).post(
params=kwargs, data=data, stream=True)
@@ -44,8 +58,8 @@ def _listen_feed(object, node, feed_reader, **kwargs):
if not line:
reader.on_heartbeat()
else:
- reader.on_message(json.loads(utils.force_text(line)))
- except exp.FeedReaderExited:
+ reader.on_message(json.loads(force_text(line)))
+ except FeedReaderExited:
reader.on_close()
@@ -100,7 +114,7 @@ class Server(object):
def __init__(self, base_url=DEFAULT_BASE_URL, full_commit=True,
authmethod="basic", verify=False):
- self.base_url, credentials = utils.extract_credentials(base_url)
+ self.base_url, credentials = extract_credentials(base_url)
self.resource = Resource(self.base_url, full_commit,
credentials=credentials,
authmethod=authmethod,
@@ -112,7 +126,7 @@ class Server(object):
def __contains__(self, name):
try:
self.resource.head(name)
- except exp.NotFound:
+ except NotFound:
return False
else:
return True
@@ -158,7 +172,7 @@ class Server(object):
"""
(r, result) = self.resource.head(name)
if r.status_code == 404:
- raise exp.NotFound("Database '{0}' does not exists".format(name))
+ raise NotFound("Database '{0}' does not exists".format(name))
db = Database(self.resource(name), name)
return db
@@ -206,7 +220,7 @@ class Server(object):
data = {'source': source, 'target': target}
data.update(kwargs)
- data = utils.force_bytes(json.dumps(data))
+ data = force_bytes(json.dumps(data))
(resp, result) = self.resource.post('_replicate', data=data)
return result
@@ -244,7 +258,7 @@ class Database(object):
try:
(resp, result) = self.resource.head(_id_to_path(doc_id))
return resp.status_code < 206
- except exp.NotFound:
+ except NotFound:
return False
def config(self):
@@ -308,14 +322,14 @@ class Database(object):
if "_deleted" not in doc:
doc["_deleted"] = True
- data = utils.force_bytes(json.dumps({"docs": _docs}))
+ data = force_bytes(json.dumps({"docs": _docs}))
params = {"all_or_nothing": "true" if transaction else "false"}
(resp, results) = self.resource.post(
"_bulk_docs", data=data, params=params)
for result, doc in zip(results, _docs):
if "error" in result:
- raise exp.Conflict("one or more docs are not saved")
+ raise Conflict("one or more docs are not saved")
return results
@@ -370,7 +384,7 @@ class Database(object):
else:
params = {}
- data = utils.force_bytes(json.dumps(_doc))
+ data = force_bytes(json.dumps(_doc))
print("gg1", flush=True)
print(data, flush=True)
@@ -390,7 +404,7 @@ class Database(object):
print("vv2", flush=True)
if resp.status_code == 409:
- raise exp.Conflict(result['reason'])
+ raise Conflict(result['reason'])
if "rev" in result and result["rev"] is not None:
_doc["_rev"] = result["rev"]
@@ -420,7 +434,7 @@ class Database(object):
if "_id" not in doc:
doc["_id"] = uuid.uuid4().hex
- data = utils.force_bytes(json.dumps({"docs": _docs}))
+ data = orce_bytes(json.dumps({"docs": _docs}))
params = {"all_or_nothing": "true" if transaction else "false"}
(resp, results) = self.resource.post("_bulk_docs", data=data,
@@ -456,9 +470,9 @@ class Database(object):
if "keys" in params:
data = {"keys": params.pop("keys")}
- data = utils.force_bytes(json.dumps(data))
+ data = force_bytes(json.dumps(data))
- params = utils.encode_view_options(params)
+ params = encode_view_options(params)
if data:
(resp, result) = self.resource.post(
"_all_docs", params=params, data=data)
@@ -538,7 +552,7 @@ class Database(object):
resource = self.resource(doc_id)
(resp, result) = resource.get(params=params)
if resp.status_code == 404:
- raise exp.NotFound("Document id `{0}` not found".format(doc_id))
+ raise NotFound("Document id `{0}` not found".format(doc_id))
for rev in result['_revs_info']:
if status and rev['status'] == status:
@@ -566,10 +580,10 @@ class Database(object):
(resp, result) = resource.delete(
filename, params={'rev': _doc['_rev']})
if resp.status_code == 404:
- raise exp.NotFound("filename {0} not found".format(filename))
+ raise NotFound("filename {0} not found".format(filename))
if resp.status_code > 205:
- raise exp.Conflict(result['reason'])
+ raise Conflict(result['reason'])
_doc['_rev'] = result['rev']
try:
@@ -645,7 +659,7 @@ class Database(object):
if resp.status_code < 206:
return self.get(doc["_id"])
- raise exp.Conflict(result['reason'])
+ raise Conflict(result['reason'])
def one(self, name, flat=None, wrapper=None, **kwargs):
"""
@@ -665,16 +679,16 @@ class Database(object):
params = {"limit": 1}
params.update(kwargs)
- path = utils._path_from_name(name, '_view')
+ path = _path_from_name(name, '_view')
data = None
if "keys" in params:
data = {"keys": params.pop('keys')}
if data:
- data = utils.force_bytes(json.dumps(data))
+ data = force_bytes(json.dumps(data))
- params = utils.encode_view_options(params)
+ params = encode_view_options(params)
result = list(self._query(self.resource(*path), wrapper=wrapper,
flat=flat, params=params, data=data))
@@ -716,16 +730,16 @@ class Database(object):
:returns: generator object
"""
params = copy.copy(kwargs)
- path = utils._path_from_name(name, '_view')
+ path = _path_from_name(name, '_view')
data = None
if "keys" in params:
data = {"keys": params.pop('keys')}
if data:
- data = utils.force_bytes(json.dumps(data))
+ data = force_bytes(json.dumps(data))
- params = utils.encode_view_options(params)
+ params = encode_view_options(params)
result = self._query(self.resource(*path), wrapper=wrapper,
flat=flat, params=params, data=data)
@@ -768,7 +782,7 @@ class Database(object):
"""
path = '_find'
- data = utils.force_bytes(json.dumps(selector))
+ data = force_bytes(json.dumps(selector))
(resp, result) = self.resource.post(path, data=data, params=kwargs)
@@ -780,7 +794,7 @@ class Database(object):
def index(self, index_doc, **kwargs):
path = '_index'
- data = utils.force_bytes(json.dumps(index_doc))
+ data = force_bytes(json.dumps(index_doc))
(resp, result) = self.resource.post(path, data=data, params=kwargs)
diff --git a/src/couch/feedreader.py b/src/couch/feedreader.py
index 98401ab..aac51d3 100644
--- a/src/couch/feedreader.py
+++ b/src/couch/feedreader.py
@@ -11,7 +11,7 @@ class BaseFeedReader:
self.db = db
return self
- def on_message(self, message):
+ def on_message(self, message: str) -> None:
"""
Callback method that is called when change
message is received from couchdb.
@@ -22,14 +22,14 @@ class BaseFeedReader:
raise NotImplementedError()
- def on_close(self):
+ def on_close(self) -> None:
"""
Callback method that is received when connection
is closed with a server. By default, does nothing.
"""
pass
- def on_heartbeat(self):
+ def on_heartbeat(self) -> None:
"""
Callback method invoked when a hearbeat (empty line) is received
from the _changes stream. Override this to purge the reader's internal
@@ -48,5 +48,5 @@ class SimpleFeedReader(BaseFeedReader):
self.callback = callback
return super(SimpleFeedReader, self).__call__(db)
- def on_message(self, message) -> None:
+ def on_message(self, message: str) -> None:
self.callback(message, db=self.db)
diff --git a/src/couch/resource.py b/src/couch/resource.py
index 364bff4..f110c8d 100644
--- a/src/couch/resource.py
+++ b/src/couch/resource.py
@@ -1,17 +1,25 @@
# -*- coding: utf-8 -*-
# Based on py-couchdb (https://github.com/histrio/py-couchdb)
-
+from __future__ import annotations
from __future__ import unicode_literals
-from typing import Union, Tuple
+from typing import Union, Tuple, Dict, Any
import json
import requests
-
-
-from couch import utils
-from couch import exceptions
+from .utils import (
+ urljoin,
+ as_json,
+ force_bytes,
+)
+from .exceptions import (
+ GenericError,
+ NotFound,
+ BadRequest,
+ Conflict,
+ AuthenticationFailed,
+)
class Resource:
@@ -40,12 +48,11 @@ class Resource:
if method == "session":
data = {"name": credentials[0], "password": credentials[1]}
- data = utils.force_bytes(json.dumps(data))
- post_url = utils.urljoin(self.base_url, "_session")
- r = self.session.post(post_url, data=data)
- if r.status_code != 200:
- raise exceptions.AuthenticationFailed()
+ post_url = urljoin(self.base_url, "_session")
+ r = self.session.post(post_url, data=force_bytes(json.dumps(data)))
+ if r and r.status_code != 200:
+ raise AuthenticationFailed()
elif method == "basic":
self.session.auth = credentials
@@ -53,8 +60,8 @@ class Resource:
else:
raise RuntimeError("Invalid authentication method")
- def __call__(self, *path: str):
- base_url = utils.urljoin(self.base_url, *path)
+ def __call__(self, *path: str) -> Resource:
+ base_url = urljoin(self.base_url, *path)
return self.__class__(base_url, session=self.session)
def _check_result(self, response, result) -> None:
@@ -68,17 +75,18 @@ class Resource:
# This is here because couchdb can return http 201
# but containing a list of conflict errors
if error == 'conflict' or error == "file_exists":
- raise exceptions.Conflict(reason or "Conflict")
+ raise Conflict(reason or "Conflict")
if response.status_code > 205:
if response.status_code == 404 or error == 'not_found':
- raise exceptions.NotFound(reason or 'Not found')
+ raise NotFound(reason or 'Not found')
elif error == 'bad_request':
- raise exceptions.BadRequest(reason or "Bad request")
- raise exceptions.GenericError(result)
+ raise BadRequest(reason or "Bad request")
+ raise GenericError(result)
- def request(self, method, path: str, params=None, data=None,
- headers=None, stream=False, **kwargs):
+
+ def request(self, method, path: Union[str, None], params=None, data=None,
+ headers=None, stream=False, **kwargs) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
if headers is None:
headers = {}
@@ -88,7 +96,7 @@ class Resource:
if path:
if not isinstance(path, (list, tuple)):
path = [path]
- url = utils.urljoin(self.base_url, *path)
+ url = urljoin(self.base_url, *path)
else:
url = self.base_url
@@ -102,7 +110,7 @@ class Resource:
result = None
self._check_result(response, result)
else:
- result = utils.as_json(response)
+ result = as_json(response)
if result is None:
return response, result
@@ -115,17 +123,17 @@ class Resource:
return response, result
- def get(self, path: Union[str, None] = None, **kwargs):
+ def get(self, path: Union[str, None] = None, **kwargs: Any) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
return self.request("GET", path, **kwargs)
- def put(self, path: Union[str, None] = None, **kwargs):
+ def put(self, path: Union[str, None] = None, **kwargs: Any) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
return self.request("PUT", path, **kwargs)
- def post(self, path: Union[str, None] = None, **kwargs):
+ def post(self, path: Union[str, None] = None, **kwargs: Any) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
return self.request("POST", path, **kwargs)
- def delete(self, path: Union[str, None] = None, **kwargs):
+ def delete(self, path: Union[str, None] = None, **kwargs: Any) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
return self.request("DELETE", path, **kwargs)
- def head(self, path: Union[str, None] = None, **kwargs):
+ def head(self, path: Union[str, None] = None, **kwargs: Any) -> Tuple[requests.models.Response, Union[Dict[str, Any], None]]:
return self.request("HEAD", path, **kwargs)
diff --git a/src/couch/utils.py b/src/couch/utils.py
index f0883a6..b3e5aa3 100644
--- a/src/couch/utils.py
+++ b/src/couch/utils.py
@@ -78,13 +78,17 @@ def urljoin(base: str, *path: str) -> str:
return reduce(_join, path, base)
# Probably bugs here
-def as_json(response: requests.models.Response) -> Union[Dict[str, Any], None]:
+def as_json(response: requests.models.Response) -> Union[Dict[str, Any], None, str]:
if "application/json" in response.headers['content-type']:
response_src = response.content.decode('utf-8')
+ print(response.content)
if response.content != b'':
ret: Dict[str, Any] = json.loads(response_src)
return ret
else:
+ print("fff")
+ print("fff")
+ print(type(response_src))
return response_src
return None