pyapiary 2.3.0__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyapiary-2.3.0 → pyapiary-2.3.1}/PKG-INFO +1 -1
- {pyapiary-2.3.0 → pyapiary-2.3.1}/pyproject.toml +1 -1
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/trino.py +2 -6
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_trino/test_unit_trino.py +46 -33
- {pyapiary-2.3.0 → pyapiary-2.3.1}/README.md +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/__init__.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/__init__.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/broker.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/domaintools.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/flashpoint.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/generic.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/ipqs.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/spycloud.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/twilio.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/api_connectors/urlscan.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/__init__.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/elasticsearch.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/mongo.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/mongo_async.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/odbc.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/postgres.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/dbms_connectors/splunk.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/helpers.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/__init__.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/conftest.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_broker/test_integration_broker.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_broker/test_unit_asyncbroker.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_broker/test_unit_broker.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/cassettes/.gitkeep +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/cassettes/test_domaintools_iris_investigate_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/cassettes/test_domaintools_parsed_whois_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/test_integration_domaintools.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/test_unit_async_domaintools.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/test_unit_domaintools.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_elasticsearch/test_unit_elasticsearch.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/cassettes/test_flashpoint_search_fraud_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_integration_flashpoint.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_unit_async_flashpoint.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_unit_flashpoint.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_generic/cassettes/test_generic_get_github_api.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_generic/test_integration_generic_connector.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_generic/test_unit_async_generic_connector.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_generic/test_unit_generic_connector.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/__init__.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/cassettes/test_ipqs_malicious_url_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/cassettes/test_ipqs_phone_validation_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/test_integration_ipqs.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/test_unit_async_ipqs.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_ipqs/test_unit_ipqs.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_mongodb/test_unit_async_mongo.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_mongodb/test_unit_mongo.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_odbc/test_unit_odbc.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_postgres/test_unit_postgres.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_splunk/test_unit_splunk.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/cassettes/test_spycloud_ato_search_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/test_integration_spycloud.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/test_unit_async_spycloud.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/test_unit_spycloud.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_twilio/cassettes/test_lookup_phone_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_twilio/test_integration_twilio.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_twilio/test_unit_async_twilio.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_twilio/test_unit_twilio.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/cassettes/test_urlscan_results_vcr.yaml +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/test_integration_urlscan.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/test_unit_async_urlscan.py +0 -0
- {pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/test_unit_urlscan.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pyapiary"
|
|
3
3
|
packages = [{ include = "pyapiary", from = "src" }]
|
|
4
|
-
version = "2.3.
|
|
4
|
+
version = "2.3.1"
|
|
5
5
|
description = "A simple, lightweight set of connectors and functions to various APIs and DBMSs, controlled by a central broker."
|
|
6
6
|
authors = ["Rob D'Aveta <rob.daveta@gmail.com>"]
|
|
7
7
|
readme = "README.md"
|
|
@@ -2,13 +2,9 @@ from trino.dbapi import connect
|
|
|
2
2
|
from typing import List, Dict, Any
|
|
3
3
|
|
|
4
4
|
class TrinoConnector:
|
|
5
|
-
def __init__(self,
|
|
5
|
+
def __init__(self, **kwargs):
|
|
6
6
|
self.conn = connect(
|
|
7
|
-
|
|
8
|
-
port=port,
|
|
9
|
-
user=user,
|
|
10
|
-
catalog=catalog,
|
|
11
|
-
schema=schema
|
|
7
|
+
**kwargs
|
|
12
8
|
)
|
|
13
9
|
|
|
14
10
|
def query(self, query_str):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
from unittest.mock import MagicMock, patch
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
3
|
from pyapiary.dbms_connectors.trino import TrinoConnector
|
|
4
4
|
|
|
5
5
|
|
|
@@ -15,11 +15,12 @@ def mock_connect(mocker):
|
|
|
15
15
|
def connector(mock_connect):
|
|
16
16
|
"""Return a TrinoConnector backed by a mocked connection."""
|
|
17
17
|
return TrinoConnector(
|
|
18
|
-
host="
|
|
19
|
-
port=
|
|
18
|
+
host="trino.trashcollector.dev",
|
|
19
|
+
port=443,
|
|
20
20
|
user="test_user",
|
|
21
21
|
catalog="hive",
|
|
22
22
|
schema="default",
|
|
23
|
+
http_scheme="https",
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
|
|
@@ -28,31 +29,33 @@ def connector(mock_connect):
|
|
|
28
29
|
# ---------------------------------------------------------------------------
|
|
29
30
|
|
|
30
31
|
class TestInit:
|
|
31
|
-
def
|
|
32
|
+
def test_connect_called_with_provided_kwargs(self, mocker):
|
|
32
33
|
mock_connect = mocker.patch("pyapiary.dbms_connectors.trino.connect")
|
|
33
|
-
TrinoConnector(host="myhost", port=
|
|
34
|
+
TrinoConnector(host="myhost", port=443, user="alice", http_scheme="https")
|
|
34
35
|
mock_connect.assert_called_once_with(
|
|
35
36
|
host="myhost",
|
|
36
|
-
port=
|
|
37
|
+
port=443,
|
|
37
38
|
user="alice",
|
|
38
|
-
|
|
39
|
-
schema="raw",
|
|
39
|
+
http_scheme="https",
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
def
|
|
42
|
+
def test_connect_called_with_minimal_kwargs(self, mocker):
|
|
43
43
|
mock_connect = mocker.patch("pyapiary.dbms_connectors.trino.connect")
|
|
44
|
-
TrinoConnector(host="myhost", port=
|
|
45
|
-
mock_connect.assert_called_once_with(
|
|
46
|
-
host="myhost",
|
|
47
|
-
port=9090,
|
|
48
|
-
user="alice",
|
|
49
|
-
catalog=None,
|
|
50
|
-
schema=None,
|
|
51
|
-
)
|
|
44
|
+
TrinoConnector(host="myhost", port=8080, user="alice")
|
|
45
|
+
mock_connect.assert_called_once_with(host="myhost", port=8080, user="alice")
|
|
52
46
|
|
|
53
47
|
def test_conn_attribute_set(self, mock_connect, connector):
|
|
54
48
|
assert connector.conn is mock_connect
|
|
55
49
|
|
|
50
|
+
def test_arbitrary_kwargs_forwarded(self, mocker):
|
|
51
|
+
"""Any kwarg the trino client supports should be forwarded as-is."""
|
|
52
|
+
mock_connect = mocker.patch("pyapiary.dbms_connectors.trino.connect")
|
|
53
|
+
TrinoConnector(host="h", port=443, user="u", http_scheme="https",
|
|
54
|
+
verify=False, session_properties={"query_max_run_time": "1h"})
|
|
55
|
+
_, call_kwargs = mock_connect.call_args
|
|
56
|
+
assert call_kwargs["verify"] is False
|
|
57
|
+
assert call_kwargs["session_properties"] == {"query_max_run_time": "1h"}
|
|
58
|
+
|
|
56
59
|
|
|
57
60
|
# ---------------------------------------------------------------------------
|
|
58
61
|
# query()
|
|
@@ -108,7 +111,17 @@ class TestQuery:
|
|
|
108
111
|
mock_connect.cursor.return_value.__enter__.return_value = mock_cursor
|
|
109
112
|
|
|
110
113
|
with pytest.raises(RuntimeError, match="syntax error"):
|
|
111
|
-
connector.query("SELECT bad
|
|
114
|
+
connector.query("SELECT bad %%")
|
|
115
|
+
|
|
116
|
+
def test_show_catalogs(self, mock_connect, connector):
|
|
117
|
+
mock_cursor = MagicMock()
|
|
118
|
+
mock_cursor.description = [("Catalog",)]
|
|
119
|
+
mock_cursor.fetchall.return_value = [("hive",), ("iceberg",), ("tpch",)]
|
|
120
|
+
mock_connect.cursor.return_value.__enter__.return_value = mock_cursor
|
|
121
|
+
|
|
122
|
+
result = connector.query("SHOW CATALOGS")
|
|
123
|
+
|
|
124
|
+
assert result == [("hive",), ("iceberg",), ("tpch",)]
|
|
112
125
|
|
|
113
126
|
|
|
114
127
|
# ---------------------------------------------------------------------------
|
|
@@ -123,27 +136,18 @@ class TestBulkInsert:
|
|
|
123
136
|
result = connector.bulk_insert("my_table", [{"id": 1, "name": "alice"}])
|
|
124
137
|
|
|
125
138
|
expected_query = "INSERT INTO my_table (id, name) VALUES (?, ?)"
|
|
126
|
-
mock_cursor.executemany.assert_called_once_with(
|
|
127
|
-
expected_query, [(1, "alice")]
|
|
128
|
-
)
|
|
139
|
+
mock_cursor.executemany.assert_called_once_with(expected_query, [(1, "alice")])
|
|
129
140
|
assert result is True
|
|
130
141
|
|
|
131
142
|
def test_inserts_multiple_rows(self, mock_connect, connector):
|
|
132
143
|
mock_cursor = MagicMock()
|
|
133
144
|
mock_connect.cursor.return_value.__enter__.return_value = mock_cursor
|
|
134
145
|
|
|
135
|
-
data = [
|
|
136
|
-
{"id": 1, "val": "a"},
|
|
137
|
-
{"id": 2, "val": "b"},
|
|
138
|
-
{"id": 3, "val": "c"},
|
|
139
|
-
]
|
|
146
|
+
data = [{"id": 1, "val": "a"}, {"id": 2, "val": "b"}, {"id": 3, "val": "c"}]
|
|
140
147
|
result = connector.bulk_insert("my_table", data)
|
|
141
148
|
|
|
142
|
-
expected_values = [(1, "a"), (2, "b"), (3, "c")]
|
|
143
|
-
_, call_values = mock_cursor.executemany.call_args
|
|
144
|
-
# positional args
|
|
145
149
|
actual_values = mock_cursor.executemany.call_args[0][1]
|
|
146
|
-
assert actual_values ==
|
|
150
|
+
assert actual_values == [(1, "a"), (2, "b"), (3, "c")]
|
|
147
151
|
assert result is True
|
|
148
152
|
|
|
149
153
|
def test_returns_none_for_empty_list(self, mock_connect, connector, capsys):
|
|
@@ -165,10 +169,10 @@ class TestBulkInsert:
|
|
|
165
169
|
mock_cursor = MagicMock()
|
|
166
170
|
mock_connect.cursor.return_value.__enter__.return_value = mock_cursor
|
|
167
171
|
|
|
168
|
-
connector.bulk_insert("
|
|
172
|
+
connector.bulk_insert("hive.default.target_table", [{"x": 99}])
|
|
169
173
|
|
|
170
174
|
actual_query = mock_cursor.executemany.call_args[0][0]
|
|
171
|
-
assert "
|
|
175
|
+
assert "hive.default.target_table" in actual_query
|
|
172
176
|
|
|
173
177
|
def test_column_order_matches_first_row_keys(self, mock_connect, connector):
|
|
174
178
|
mock_cursor = MagicMock()
|
|
@@ -178,10 +182,19 @@ class TestBulkInsert:
|
|
|
178
182
|
connector.bulk_insert("t", data)
|
|
179
183
|
|
|
180
184
|
actual_query = mock_cursor.executemany.call_args[0][0]
|
|
181
|
-
# columns in query should match key order of first dict
|
|
182
185
|
for col in ["z", "a", "m"]:
|
|
183
186
|
assert col in actual_query
|
|
184
187
|
|
|
188
|
+
def test_placeholder_count_matches_column_count(self, mock_connect, connector):
|
|
189
|
+
mock_cursor = MagicMock()
|
|
190
|
+
mock_connect.cursor.return_value.__enter__.return_value = mock_cursor
|
|
191
|
+
|
|
192
|
+
data = [{"a": 1, "b": 2, "c": 3, "d": 4}]
|
|
193
|
+
connector.bulk_insert("t", data)
|
|
194
|
+
|
|
195
|
+
actual_query = mock_cursor.executemany.call_args[0][0]
|
|
196
|
+
assert actual_query.count("?") == 4
|
|
197
|
+
|
|
185
198
|
def test_propagates_executemany_exception(self, mock_connect, connector):
|
|
186
199
|
mock_cursor = MagicMock()
|
|
187
200
|
mock_cursor.executemany.side_effect = RuntimeError("DB write error")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/test_unit_async_domaintools.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_domaintools/test_unit_domaintools.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_elasticsearch/test_unit_elasticsearch.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_integration_flashpoint.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_unit_async_flashpoint.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_flashpoint/test_unit_flashpoint.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_generic/test_unit_generic_connector.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/test_integration_spycloud.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_spycloud/test_unit_async_spycloud.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/test_integration_urlscan.py
RENAMED
|
File without changes
|
{pyapiary-2.3.0 → pyapiary-2.3.1}/src/pyapiary/tests/test_urlscan/test_unit_async_urlscan.py
RENAMED
|
File without changes
|
|
File without changes
|