From 9bf16991e7d65429d967273f55462366091934de Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 31 Oct 2024 21:24:50 +0100 Subject: [PATCH] Chore: Format code using Ruff. Satisfy and enable linter on CI. (#26) --- .github/workflows/tests.yml | 2 +- cratedb.py | 57 +++++----- examples/example_usage.py | 25 ++--- examples/object_examples.py | 207 ++++++++++++++++-------------------- examples/picow_demo.py | 26 +++-- pyproject.toml | 7 +- tests/test_examples.py | 2 + 7 files changed, 149 insertions(+), 177 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f5e0685..8c01b36 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,7 +55,7 @@ jobs: run: uv pip install --system --requirement=requirements.txt --requirement=requirements-dev.txt - name: Run linters and software tests - run: poe test + run: poe check # https://github.com/codecov/codecov-action - name: Upload coverage results to Codecov diff --git a/cratedb.py b/cratedb.py index 336ffb3..0b2f0cf 100644 --- a/cratedb.py +++ b/cratedb.py @@ -2,7 +2,7 @@ import requests except ImportError: import urequests as requests - + from base64 import b64encode # IDs of CrateDB supported data types. @@ -78,14 +78,19 @@ CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004 CRATEDB_ERROR_QUERY_KILLED = 5030 + class NetworkError(Exception): pass + class CrateDBError(Exception): pass + class CrateDB: - def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True): + def __init__( + self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True + ): self.user = user self.password = password self.schema = schema @@ -93,31 +98,26 @@ def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ self.port = port self.use_ssl = use_ssl - self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql" + self.cratedb_url = f"{'https' if self.use_ssl is True else 'http'}://{self.host}:{self.port}/_sql" if self.user is not None and self.password is not None: self.encoded_credentials = self.__encode_credentials(self.user, self.password) - def __encode_credentials(self, user, password): creds_str = f"{user}:{password}" return b64encode(creds_str.encode("UTF-8")).decode("UTF-8") - - def __make_request(self, sql, args=None, with_types = False, return_response = True): - headers = { - "Content-Type": "text/json", - "Default-Schema": self.schema - } + def __make_request(self, sql, args=None, with_types=False, return_response=True): + headers = {"Content-Type": "text/json", "Default-Schema": self.schema} if hasattr(self, "encoded_credentials"): headers["Authorization"] = f"Basic {self.encoded_credentials}" - request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types" + request_url = ( + self.cratedb_url if with_types is False else f"{self.cratedb_url}?types" + ) - payload = { - "stmt": sql - } + payload = {"stmt": sql} if args is not None: for arg in args: @@ -125,28 +125,29 @@ def __make_request(self, sql, args=None, with_types = False, return_response = T payload["args"] = args break - if not "args" in payload: + if "args" not in payload: payload["bulk_args"] = args try: - response = requests.post( - request_url, - headers = headers, - json = payload - ) + response = requests.post(request_url, headers=headers, json=payload) except OSError as o: - raise NetworkError(o) + raise NetworkError(o) # noqa: B904 - if response.status_code == 400 or response.status_code == 404 or response.status_code == 409: + if ( + response.status_code == 400 + or response.status_code == 404 + or response.status_code == 409 + ): error_doc = response.json() raise CrateDBError(error_doc) - elif response.status_code != 200: - raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}") + if response.status_code != 200: + raise NetworkError( + f"Error {response.status_code}: {response.reason.decode('UTF-8')}" + ) - if return_response == True: + if return_response is True: return response.json() + return None - - def execute(self, sql, args = None, with_types = False, return_response = True): + def execute(self, sql, args=None, with_types=False, return_response=True): return self.__make_request(sql, args, with_types, return_response) - \ No newline at end of file diff --git a/examples/example_usage.py b/examples/example_usage.py index 3d748ae..7ac577a 100644 --- a/examples/example_usage.py +++ b/examples/example_usage.py @@ -1,5 +1,7 @@ +# ruff: noqa: W291 Trailing whitespace + # Example script showing different types of interactions with -# CrateDB. This has no hardware dependencies so should run +# CrateDB. This has no hardware dependencies so should run # in any MicroPython environment. You will need to edit the # code below to use your CrateDB credentials. @@ -20,7 +22,9 @@ try: # Create a table. print("Create table.") - response = crate.execute("create table driver_test(id TEXT, val1 bigint, val2 bigint, val3 boolean)") + response = crate.execute( + "create table driver_test(id TEXT, val1 bigint, val2 bigint, val3 boolean)" + ) # response: # {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 119.652275} print(response) @@ -29,10 +33,7 @@ print("Bulk insert.") response = crate.execute( "insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)", - [ - [ "a", 2, 3, True ], - [ "b", 3, 4, False ] - ] + [["a", 2, 3, True], ["b", 3, 4, False]], ) # response: # {'results': [{'rowcount': 1}, {'rowcount': 1}], 'cols': [], 'duration': 6.265751} @@ -42,14 +43,14 @@ print("Select with column data types.") response = crate.execute("select * from driver_test", with_types=True) # response: - # {'col_types': [4, 10, 10, 3], 'cols': ['id', 'val1', 'val2', 'val3'], 'rowcount': 2, 'rows': [['b', 3, 4, False], ['a', 2, 3, True]], 'duration': 4.378391} + # {'col_types': [4, 10, 10, 3], 'cols': ['id', 'val1', 'val2', 'val3'], 'rowcount': 2, + # 'rows': [['b', 3, 4, False], ['a', 2, 3, True]], 'duration': 4.378391} print(response) # SELECT with parameter substitution. print("Select with parameter substitution.") response = crate.execute( - "select val1, val2 from driver_test where val1 > ? and val2 < ?", - [ 1, 4 ] + "select val1, val2 from driver_test where val1 > ? and val2 < ?", [1, 4] ) # response: # {'rows': [[2, 3]], 'rowcount': 1, 'cols': ['val1', 'val2'], 'duration': 3.266117} @@ -59,7 +60,7 @@ print("Insert with parameter substitution.") response = crate.execute( "insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)", - [ "d", 1, 9, False ] + ["d", 1, 9, False], ) # response: # {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 5.195949} @@ -69,8 +70,8 @@ print("Insert with parameter substitution and no response processing.") response = crate.execute( "insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)", - [ "e", 4, 12, True ], - return_response=False + ["e", 4, 12, True], + return_response=False, ) # response: # None diff --git a/examples/object_examples.py b/examples/object_examples.py index 9b9b1ce..2905a5c 100644 --- a/examples/object_examples.py +++ b/examples/object_examples.py @@ -1,5 +1,7 @@ +# ruff: noqa: W291 Trailing whitespace + # Example script showing different types of interactions with -# objects in CrateDB. This has no hardware dependencies so should +# objects in CrateDB. This has no hardware dependencies so should # run in any MicroPython environment. You will need to edit the # code below to use your CrateDB credentials. @@ -23,7 +25,9 @@ # Create a table, using a dynamic object column. print("Create table.") - response = crate.execute("CREATE TABLE driver_object_test (id TEXT PRIMARY KEY, data OBJECT(DYNAMIC))") + response = crate.execute( + "CREATE TABLE driver_object_test (id TEXT PRIMARY KEY, data OBJECT(DYNAMIC))" + ) # response: # {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 270.4579} @@ -32,45 +36,36 @@ # Insert an object with arbitrary complexity. print("INSERT a row containing an object.") example_obj = { - "sensor_readings": { - "temp": 23.3, - "humidity": 61.2 - }, + "sensor_readings": {"temp": 23.3, "humidity": 61.2}, "metadata": { "software_version": "1.19", "battery_percentage": 57, - "uptime": 2851200 - } + "uptime": 2851200, + }, } response = crate.execute( - "INSERT INTO driver_object_test (id, data) VALUES (?, ?)", - [ - "2cae54", - example_obj - ] + "INSERT INTO driver_object_test (id, data) VALUES (?, ?)", ["2cae54", example_obj] ) - # response: + # response: # {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 334.37607} print(response) # Select query returning the entire object. print("SELECT the whole object.") response = crate.execute( - "SELECT data FROM driver_object_test WHERE id = ?", - [ - "2cae54" - ] + "SELECT data FROM driver_object_test WHERE id = ?", ["2cae54"] ) # response: # {'rows': [ - # [{ - # 'metadata': {'software_version': '1.19', 'uptime': 2851200, 'battery_percentage': 57}, - # 'sensor_readings': {'humidity': 61.2, 'temp': 23.3}} - # ] - # ], 'rowcount': 1, 'cols': ['data'], 'duration': 21.946375 + # [{ + # 'metadata': {'software_version': '1.19', 'uptime': 2851200, + # 'battery_percentage': 57}, + # 'sensor_readings': {'humidity': 61.2, 'temp': 23.3}} + # ] + # ], 'rowcount': 1, 'cols': ['data'], 'duration': 21.946375 # } print(response) @@ -83,15 +78,13 @@ data['sensor_readings'] AS sensor_readings FROM driver_object_test WHERE id = ?""", - [ - "2cae54" - ] + ["2cae54"], ) # response: # {'rows': [ # [2851200, {'humidity': 61.2, 'temp': 23.3}] - # ], + # ], # 'rowcount': 1, 'cols': ['uptime', 'sensor_readings'], 'duration': 4.047666 # } print(response) @@ -100,55 +93,46 @@ response = crate.execute( "INSERT INTO driver_object_test (id, data) VALUES (?, ?)", [ - [ - "2cae56", + [ + "2cae56", { - "sensor_readings": { - "temp": 21.8, - "humidity": 57.9 - }, + "sensor_readings": {"temp": 21.8, "humidity": 57.9}, "metadata": { "software_version": "1.19", "battery_percentage": 43, - "uptime": 2851643 - } - } + "uptime": 2851643, + }, + }, ], [ - "1d452b", + "1d452b", { - "sensor_readings": { - "temp": 11.4, - "humidity": 43.9 - }, + "sensor_readings": {"temp": 11.4, "humidity": 43.9}, "metadata": { "software_version": "1.20", "battery_percentage": 17, - "uptime": 853436 - } - } + "uptime": 853436, + }, + }, ], - [ - "4e000f", + [ + "4e000f", { - "sensor_readings": { - "temp": 26.8, - "humidity": 78.9 - }, + "sensor_readings": {"temp": 26.8, "humidity": 78.9}, "metadata": { "software_version": "1.19", "battery_percentage": 84, - "uptime": 1468356 + "uptime": 1468356, }, - } - ] - ] + }, + ], + ], ) # response: # {'results': [ - # {'rowcount': 1}, - # {'rowcount': 1}, + # {'rowcount': 1}, + # {'rowcount': 1}, # {'rowcount': 1} # ], 'cols': [], 'duration': 4.426417 # } @@ -156,12 +140,11 @@ # Tells the database to refresh the table. This is here to make sure that the # subsequent SELECT has the data from the INSERT above. This is not needed in - # real application code. + # real application code. # See https://cratedb.com/docs/crate/reference/en/latest/sql/statements/refresh.html # for details. crate.execute("REFRESH TABLE driver_object_test", return_response=False) - # SELECT example that filters by an object property in the WHERE clause. print("SELECT and filter by an object property in the WHERE clause.") response = crate.execute( @@ -173,8 +156,8 @@ # response: # { 'rows': [ - # ['4e000f', {'humidity': 78.90000000000001, 'temp': 26.8}], - # ['2cae54', {'humidity': 61.2, 'temp': 23.3}], + # ['4e000f', {'humidity': 78.90000000000001, 'temp': 26.8}], + # ['2cae54', {'humidity': 61.2, 'temp': 23.3}], # ['2cae56', {'humidity': 57.9, 'temp': 21.8}] # ], 'rowcount': 3, 'cols': ['id', 'sensor_readings'], 'duration': 13.790875} print(response) @@ -191,58 +174,39 @@ "co", { "name": "Colombia", - "borders": [ - "Brazil", "Peru", "Panama", "Ecuador", "Venezuela" - ], - "currency": { - "code": "COP", - "name": "Colobian Peso" - }, + "borders": ["Brazil", "Peru", "Panama", "Ecuador", "Venezuela"], + "currency": {"code": "COP", "name": "Colobian Peso"}, "rates": [ - { - "code": "USD", - "rate": "0.000235" - }, - { - "code": "EUR", - "rate": "0.000216" - }, - { - "code": "JPY", - "rate": "0.035154" - } - ] - } + {"code": "USD", "rate": "0.000235"}, + {"code": "EUR", "rate": "0.000216"}, + {"code": "JPY", "rate": "0.035154"}, + ], + }, ], [ "br", { "name": "Brazil", "borders": [ - "Argentina", "Bolivia", "Colombia", "French Guiana", - "Guyana", "Paraguay", "Suriname", "Uruguay", "Venezuela" + "Argentina", + "Bolivia", + "Colombia", + "French Guiana", + "Guyana", + "Paraguay", + "Suriname", + "Uruguay", + "Venezuela", ], - "currency": { - "code": "BRL", - "name": "Brazilian Real" - }, + "currency": {"code": "BRL", "name": "Brazilian Real"}, "rates": [ - { - "code": "USD", - "rate": "0.176620" - }, - { - "code": "EUR", - "rate": "0.162409" - }, - { - "code": "JPY", - "rate": "26.432108" - } - ] - } - ] - ] + {"code": "USD", "rate": "0.176620"}, + {"code": "EUR", "rate": "0.162409"}, + {"code": "JPY", "rate": "26.432108"}, + ], + }, + ], + ], ) # response: @@ -251,17 +215,19 @@ # Some examples showing how to access data inside arrays. # See also https://cratedb.com/docs/crate/reference/en/latest/general/builtins/scalar-functions.html#array-functions - + # Simple array... which countries does Colombia share a border with? print("Arrays... countries sharing a border with Colombia.") response = crate.execute( """ - SELECT data['borders'] as shares_border_with FROM driver_object_test WHERE id = 'co' + SELECT data['borders'] as shares_border_with + FROM driver_object_test WHERE id = 'co' """ ) # response: - # {'rows': [[['Brazil', 'Peru', 'Panama', 'Ecuador', 'Venezuela']]], 'rowcount': 1, 'cols': ['shares_border_with'], 'duration': 50.105873} + # {'rows': [[['Brazil', 'Peru', 'Panama', 'Ecuador', 'Venezuela']]], + # 'rowcount': 1, 'cols': ['shares_border_with'], 'duration': 50.105873} print(response) # How many countries does Brazil share a border with? @@ -269,7 +235,8 @@ response = crate.execute( # 1 here is the array dimension to get the length of, as arrays can be nested. """ - SELECT array_length(data['borders'], 1) as how_many FROM driver_object_test WHERE id = 'br' + SELECT array_length(data['borders'], 1) AS how_many + FROM driver_object_test WHERE id = 'br' """ ) @@ -277,16 +244,19 @@ # {'rows': [[9]], 'rowcount': 1, 'cols': ['how_many'], 'duration': 1.397291} print(response) - # What are the 2nd, 3rd and 4th countries in Brazil's borders array? (Arrays are 1 indexed). + # What are the 2nd, 3rd and 4th countries in Brazil's borders array? + # Arrays are 1-indexed. print("Array slicing.") response = crate.execute( """ - SELECT array_slice(data['borders'], 2, 4) AS slice FROM driver_object_test WHERE id = 'br' + SELECT array_slice(data['borders'], 2, 4) AS slice + FROM driver_object_test WHERE id = 'br' """ ) # response: - # {'rows': [[['Bolivia', 'Colombia', 'French Guiana']]], 'rowcount': 1, 'cols': ['slice'], 'duration': 1.517417} + # {'rows': [[['Bolivia', 'Colombia', 'French Guiana']]], 'rowcount': 1, + # 'cols': ['slice'], 'duration': 1.517417} print(response) # Example queries for arrays containing objects. @@ -296,28 +266,31 @@ print("Mixing objects and arrays...") response = crate.execute( """ - SELECT data['rates'][2]['code'] FROM driver_object_test WHERE id='co' + SELECT data['rates'][2]['code'] + FROM driver_object_test WHERE id='co' """ ) # response: - # {'rows': [['EUR']], 'rowcount': 1, 'cols': ["data[2]['rates']['code']"], 'duration': 0.553541} + # {'rows': [['EUR']], 'rowcount': 1, + # 'cols': ["data[2]['rates']['code']"], 'duration': 0.553541} print(response) - # Array comparisons. + # Array comparisons. # https://cratedb.com/docs/crate/reference/en/latest/general/builtins/array-comparisons.html#sql-array-comparisons # Which countries share a border with Paraguay? print("Element in array: Who shares a border with Paraguay?") response = crate.execute( """ - SELECT data['name'] AS name, 'Paraguay' IN (data['borders']) AS borders_paraguay - FROM driver_object_test WHERE id IN ('br', 'co') + SELECT data['name'] AS name, 'Paraguay' IN (data['borders']) AS borders_paraguay + FROM driver_object_test WHERE id IN ('br', 'co') """ ) # response: - # {'rows': [['Colombia', False], ['Brazil', True]], 'rowcount': 2, 'cols': ['name', 'borders_paraguay'], 'duration': 0.574833} + # {'rows': [['Colombia', False], ['Brazil', True]], 'rowcount': 2, + # 'cols': ['name', 'borders_paraguay'], 'duration': 0.574833} print(response) # Drop the table. @@ -332,4 +305,4 @@ print(e) except cratedb.CrateDBError as c: print("Caught CrateDBError:") - print(c) \ No newline at end of file + print(c) diff --git a/examples/picow_demo.py b/examples/picow_demo.py index bad62e9..a2d7d7d 100644 --- a/examples/picow_demo.py +++ b/examples/picow_demo.py @@ -1,12 +1,15 @@ +# ruff: noqa: W291 Trailing whitespace + # micropython-cratedb Example for the Raspberry Pi Pico W. # Configure your CrateDB credentials and WiFi SSID # and password below before running this. -import machine -import network import sys import time +import machine +import network + import cratedb # CrateDB Docker / local network, no SSL. @@ -42,8 +45,8 @@ ts TIMESTAMP WITH TIME ZONE GENERATED ALWAYS AS current_timestamp, temp DOUBLE PRECISION ) - """, - return_response = False + """, + return_response=False, ) except Exception as e: @@ -63,15 +66,11 @@ # https://www.coderdojotc.org/micropython/advanced-labs/03-internal-temperature/ sensor_temp = machine.ADC(4) reading = sensor_temp.read_u16() * (3.3 / (65535)) - temperature = 27 - (reading - 0.706)/0.001721 + temperature = 27 - (reading - 0.706) / 0.001721 temperature = round(temperature, 1) response = crate.execute( - "INSERT INTO picow_test (id, temp) VALUES (?, ?)", - [ - ip_addr, - temperature - ] + "INSERT INTO picow_test (id, temp) VALUES (?, ?)", [ip_addr, temperature] ) if response["rowcount"] == 1: @@ -81,10 +80,9 @@ # Every 10th iteration let's read back an average value for the last 24hrs. if num_iterations == 10: response = crate.execute( - "SELECT trunc(avg(temp), 1) AS avg_temp FROM picow_test WHERE id=? AND ts >= (CURRENT_TIMESTAMP - INTERVAL '1' DAY)", - [ - ip_addr - ] + "SELECT trunc(avg(temp), 1) AS avg_temp " + "FROM picow_test WHERE id=? AND ts >= (CURRENT_TIMESTAMP - INTERVAL '1' DAY)", + [ip_addr], ) if response["rowcount"] == 1: diff --git a/pyproject.toml b/pyproject.toml index 444e1b6..539f7f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.ruff] -line-length = 100 +line-length = 90 extend-exclude = [ ] @@ -45,7 +45,7 @@ lint.per-file-ignores."examples/*" = [ ] lint.per-file-ignores."tests/*" = [ - "S101", # Allow use of `assert` + "S101", # Allow use of `assert` ] [tool.pytest.ini_options] @@ -59,7 +59,6 @@ log_cli_level = "DEBUG" log_format = "%(asctime)-15s [%(name)-36s] %(levelname)-8s: %(message)s" xfail_strict = true - [tool.coverage.paths2] source = [ ".", @@ -84,7 +83,6 @@ exclude_lines = [ "raise NotImplemented", ] - # =================== # Tasks configuration # =================== @@ -110,7 +108,6 @@ lint = [ { cmd = "validate-pyproject pyproject.toml" }, ] - [tool.poe.tasks.test] cmd = "pytest" help = "Invoke software tests" diff --git a/tests/test_examples.py b/tests/test_examples.py index d5dd5f6..6b2f148 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,3 +1,5 @@ +# ruff: noqa: S603, S607 + import os import subprocess from pathlib import Path